summaryrefslogtreecommitdiffstats
path: root/source3/smbd/smb2_nttrans.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/smbd/smb2_nttrans.c')
-rw-r--r--source3/smbd/smb2_nttrans.c911
1 files changed, 911 insertions, 0 deletions
diff --git a/source3/smbd/smb2_nttrans.c b/source3/smbd/smb2_nttrans.c
new file mode 100644
index 0000000..49bddf5
--- /dev/null
+++ b/source3/smbd/smb2_nttrans.c
@@ -0,0 +1,911 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB NT transaction handling
+ Copyright (C) Jeremy Allison 1994-2007
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ 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
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/smbd.h"
+#include "smbd/globals.h"
+#include "fake_file.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "passdb/lookup_sid.h"
+#include "auth.h"
+#include "smbprofile.h"
+#include "libsmb/libsmb.h"
+#include "lib/util_ea.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_security.h"
+
+extern const struct generic_mapping file_generic_mapping;
+
+/*********************************************************************
+ Windows seems to do canonicalization of inheritance bits. Do the
+ same.
+*********************************************************************/
+
+static void canonicalize_inheritance_bits(struct files_struct *fsp,
+ struct security_descriptor *psd)
+{
+ bool set_auto_inherited = false;
+
+ /*
+ * We need to filter out the
+ * SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ
+ * bits. If both are set we store SEC_DESC_DACL_AUTO_INHERITED
+ * as this alters whether SEC_ACE_FLAG_INHERITED_ACE is set
+ * when an ACE is inherited. Otherwise we zero these bits out.
+ * See:
+ *
+ * http://social.msdn.microsoft.com/Forums/eu/os_fileservices/thread/11f77b68-731e-407d-b1b3-064750716531
+ *
+ * for details.
+ */
+
+ if (!lp_acl_flag_inherited_canonicalization(SNUM(fsp->conn))) {
+ psd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ;
+ return;
+ }
+
+ if ((psd->type & (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ))
+ == (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) {
+ set_auto_inherited = true;
+ }
+
+ psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ);
+ if (set_auto_inherited) {
+ psd->type |= SEC_DESC_DACL_AUTO_INHERITED;
+ }
+}
+
+/****************************************************************************
+ Internal fn to set security descriptors.
+****************************************************************************/
+
+NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd,
+ uint32_t security_info_sent)
+{
+ files_struct *sd_fsp = NULL;
+ NTSTATUS status;
+
+ if (!CAN_WRITE(fsp->conn)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (!lp_nt_acl_support(SNUM(fsp->conn))) {
+ return NT_STATUS_OK;
+ }
+
+ status = refuse_symlink_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("ACL set on symlink %s denied.\n",
+ fsp_str_dbg(fsp));
+ return status;
+ }
+
+ if (psd->owner_sid == NULL) {
+ security_info_sent &= ~SECINFO_OWNER;
+ }
+ if (psd->group_sid == NULL) {
+ security_info_sent &= ~SECINFO_GROUP;
+ }
+
+ /* Ensure we have at least one thing set. */
+ if ((security_info_sent & (SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL)) == 0) {
+ /* Just like W2K3 */
+ return NT_STATUS_OK;
+ }
+
+ /* Ensure we have the rights to do this. */
+ if (security_info_sent & SECINFO_OWNER) {
+ status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (security_info_sent & SECINFO_GROUP) {
+ status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ if (security_info_sent & SECINFO_DACL) {
+ status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /* Convert all the generic bits. */
+ if (psd->dacl) {
+ security_acl_map_generic(psd->dacl, &file_generic_mapping);
+ }
+ }
+
+ if (security_info_sent & SECINFO_SACL) {
+ status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /*
+ * Setting a SACL also requires WRITE_DAC.
+ * See the smbtorture3 SMB2-SACL test.
+ */
+ status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /* Convert all the generic bits. */
+ if (psd->sacl) {
+ security_acl_map_generic(psd->sacl, &file_generic_mapping);
+ }
+ }
+
+ canonicalize_inheritance_bits(fsp, psd);
+
+ if (DEBUGLEVEL >= 10) {
+ DEBUG(10,("set_sd for file %s\n", fsp_str_dbg(fsp)));
+ NDR_PRINT_DEBUG(security_descriptor, psd);
+ }
+
+ sd_fsp = metadata_fsp(fsp);
+ status = SMB_VFS_FSET_NT_ACL(sd_fsp, security_info_sent, psd);
+
+ TALLOC_FREE(psd);
+
+ return status;
+}
+
+static bool check_smb2_posix_chmod_ace(const struct files_struct *fsp,
+ uint32_t security_info_sent,
+ struct security_descriptor *psd,
+ mode_t *pmode)
+{
+ int cmp;
+
+ /*
+ * This must be an ACL with one ACE containing an
+ * MS NFS style mode entry coming in on a POSIX
+ * handle over SMB2+.
+ */
+ if (!fsp->conn->sconn->using_smb2) {
+ return false;
+ }
+
+ if (!(fsp->posix_flags & FSP_POSIX_FLAGS_OPEN)) {
+ return false;
+ }
+
+ if (!(security_info_sent & SECINFO_DACL)) {
+ return false;
+ }
+
+ if (psd->dacl == NULL) {
+ return false;
+ }
+
+ if (psd->dacl->num_aces != 1) {
+ return false;
+ }
+
+ cmp = dom_sid_compare_domain(&global_sid_Unix_NFS_Mode,
+ &psd->dacl->aces[0].trustee);
+ if (cmp != 0) {
+ return false;
+ }
+
+ *pmode = (mode_t)psd->dacl->aces[0].trustee.sub_auths[2];
+ *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
+
+ return true;
+}
+
+/****************************************************************************
+ Internal fn to set security descriptors from a data blob.
+****************************************************************************/
+
+NTSTATUS set_sd_blob(files_struct *fsp, uint8_t *data, uint32_t sd_len,
+ uint32_t security_info_sent)
+{
+ struct security_descriptor *psd = NULL;
+ NTSTATUS status;
+ bool do_chmod = false;
+ mode_t smb2_posix_mode = 0;
+ int ret;
+
+ if (sd_len == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = unmarshall_sec_desc(talloc_tos(), data, sd_len, &psd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ do_chmod = check_smb2_posix_chmod_ace(fsp,
+ security_info_sent,
+ psd,
+ &smb2_posix_mode);
+ if (!do_chmod) {
+ return set_sd(fsp, psd, security_info_sent);
+ }
+
+ TALLOC_FREE(psd);
+
+ ret = SMB_VFS_FCHMOD(fsp, smb2_posix_mode);
+ if (ret != 0) {
+ status = map_nt_error_from_unix(errno);
+ DBG_ERR("smb2_posix_chmod [%s] [%04o] failed: %s\n",
+ fsp_str_dbg(fsp),
+ (unsigned)smb2_posix_mode,
+ nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Copy a file.
+****************************************************************************/
+
+NTSTATUS copy_internals(TALLOC_CTX *ctx,
+ connection_struct *conn,
+ struct smb_request *req,
+ struct files_struct *src_dirfsp,
+ struct smb_filename *smb_fname_src,
+ struct files_struct *dst_dirfsp,
+ struct smb_filename *smb_fname_dst,
+ uint32_t attrs)
+{
+ files_struct *fsp1,*fsp2;
+ uint32_t fattr;
+ int info;
+ off_t ret=-1;
+ NTSTATUS status = NT_STATUS_OK;
+ struct smb_filename *parent = NULL;
+ struct smb_filename *pathref = NULL;
+
+ if (!CAN_WRITE(conn)) {
+ status = NT_STATUS_MEDIA_WRITE_PROTECTED;
+ goto out;
+ }
+
+ /* Source must already exist. */
+ if (!VALID_STAT(smb_fname_src->st)) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ goto out;
+ }
+
+ /* Ensure attributes match. */
+ fattr = fdos_mode(smb_fname_src->fsp);
+ if ((fattr & ~attrs) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) {
+ status = NT_STATUS_NO_SUCH_FILE;
+ goto out;
+ }
+
+ /* Disallow if dst file already exists. */
+ if (VALID_STAT(smb_fname_dst->st)) {
+ status = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto out;
+ }
+
+ /* No links from a directory. */
+ if (S_ISDIR(smb_fname_src->st.st_ex_mode)) {
+ status = NT_STATUS_FILE_IS_A_DIRECTORY;
+ goto out;
+ }
+
+ DEBUG(10,("copy_internals: doing file copy %s to %s\n",
+ smb_fname_str_dbg(smb_fname_src),
+ smb_fname_str_dbg(smb_fname_dst)));
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ src_dirfsp, /* dirfsp */
+ smb_fname_src, /* fname */
+ FILE_READ_DATA|FILE_READ_ATTRIBUTES|
+ FILE_READ_EA, /* access_mask */
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
+ FILE_SHARE_DELETE),
+ FILE_OPEN, /* create_disposition*/
+ 0, /* create_options */
+ FILE_ATTRIBUTE_NORMAL, /* file_attributes */
+ NO_OPLOCK, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp1, /* result */
+ &info, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ conn, /* conn */
+ req, /* req */
+ dst_dirfsp, /* dirfsp */
+ smb_fname_dst, /* fname */
+ FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|
+ FILE_WRITE_EA, /* access_mask */
+ (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
+ FILE_SHARE_DELETE),
+ FILE_CREATE, /* create_disposition*/
+ 0, /* create_options */
+ fattr, /* file_attributes */
+ NO_OPLOCK, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp2, /* result */
+ &info, /* pinfo */
+ NULL, NULL); /* create context */
+
+ if (!NT_STATUS_IS_OK(status)) {
+ close_file_free(NULL, &fsp1, ERROR_CLOSE);
+ goto out;
+ }
+
+ if (smb_fname_src->st.st_ex_size) {
+ ret = vfs_transfer_file(fsp1, fsp2, smb_fname_src->st.st_ex_size);
+ }
+
+ /*
+ * As we are opening fsp1 read-only we only expect
+ * an error on close on fsp2 if we are out of space.
+ * Thus we don't look at the error return from the
+ * close of fsp1.
+ */
+ close_file_free(NULL, &fsp1, NORMAL_CLOSE);
+
+ /* Ensure the modtime is set correctly on the destination file. */
+ set_close_write_time(fsp2, smb_fname_src->st.st_ex_mtime);
+
+ status = close_file_free(NULL, &fsp2, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("close_file_free() failed: %s\n",
+ nt_errstr(status));
+ /*
+ * We can't do much but leak the fsp
+ */
+ goto out;
+ }
+
+ /* Grrr. We have to do this as open_file_ntcreate adds FILE_ATTRIBUTE_ARCHIVE when it
+ creates the file. This isn't the correct thing to do in the copy
+ case. JRA */
+
+ status = SMB_VFS_PARENT_PATHNAME(conn,
+ talloc_tos(),
+ smb_fname_dst,
+ &parent,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ if (smb_fname_dst->fsp == NULL) {
+ status = synthetic_pathref(parent,
+ conn->cwd_fsp,
+ smb_fname_dst->base_name,
+ smb_fname_dst->stream_name,
+ NULL,
+ smb_fname_dst->twrp,
+ smb_fname_dst->flags,
+ &pathref);
+
+ /* should we handle NT_STATUS_OBJECT_NAME_NOT_FOUND specially here ???? */
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(parent);
+ goto out;
+ }
+ file_set_dosmode(conn, pathref, fattr, parent, false);
+ smb_fname_dst->st.st_ex_mode = pathref->st.st_ex_mode;
+ } else {
+ file_set_dosmode(conn, smb_fname_dst, fattr, parent, false);
+ }
+ TALLOC_FREE(parent);
+
+ if (ret < (off_t)smb_fname_src->st.st_ex_size) {
+ status = NT_STATUS_DISK_FULL;
+ goto out;
+ }
+ out:
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(3,("copy_internals: Error %s copy file %s to %s\n",
+ nt_errstr(status), smb_fname_str_dbg(smb_fname_src),
+ smb_fname_str_dbg(smb_fname_dst)));
+ }
+
+ return status;
+}
+
+/******************************************************************************
+ Fake up a completely empty SD.
+*******************************************************************************/
+
+static NTSTATUS get_null_nt_acl(TALLOC_CTX *mem_ctx, struct security_descriptor **ppsd)
+{
+ size_t sd_size;
+
+ *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size);
+ if(!*ppsd) {
+ DEBUG(0,("get_null_nt_acl: Unable to malloc space for security descriptor.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Get a security descriptor from the file system, normalize for components
+ requested.
+****************************************************************************/
+
+static NTSTATUS smbd_fetch_security_desc(connection_struct *conn,
+ TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ uint32_t security_info_wanted,
+ struct security_descriptor **ppsd)
+{
+ NTSTATUS status;
+ struct security_descriptor *psd = NULL;
+ bool need_to_read_sd = false;
+
+ /*
+ * Get the permissions to return.
+ */
+
+ if (security_info_wanted & SECINFO_SACL) {
+ status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Access to SACL denied.\n");
+ return status;
+ }
+ }
+
+ if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|SECINFO_GROUP)) {
+ status = check_any_access_fsp(fsp, SEC_STD_READ_CONTROL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("Access to DACL, OWNER, or GROUP denied.\n");
+ return status;
+ }
+ }
+
+ status = refuse_symlink_fsp(fsp);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_DEBUG("ACL get on symlink %s denied.\n",
+ fsp_str_dbg(fsp));
+ return status;
+ }
+
+ if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|
+ SECINFO_GROUP|SECINFO_SACL)) {
+ /* Don't return SECINFO_LABEL if anything else was
+ requested. See bug #8458. */
+ security_info_wanted &= ~SECINFO_LABEL;
+
+ /*
+ * Only query the file system SD if the caller asks
+ * for any bits. This allows a caller to open without
+ * READ_CONTROL but still issue a query sd. See
+ * smb2.sdread test.
+ */
+ need_to_read_sd = true;
+ }
+
+ if (lp_nt_acl_support(SNUM(conn)) &&
+ ((security_info_wanted & SECINFO_LABEL) == 0) &&
+ need_to_read_sd)
+ {
+ files_struct *sd_fsp = metadata_fsp(fsp);
+ status = SMB_VFS_FGET_NT_ACL(
+ sd_fsp, security_info_wanted, mem_ctx, &psd);
+ } else {
+ status = get_null_nt_acl(mem_ctx, &psd);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(security_info_wanted & SECINFO_OWNER)) {
+ psd->owner_sid = NULL;
+ }
+ if (!(security_info_wanted & SECINFO_GROUP)) {
+ psd->group_sid = NULL;
+ }
+ if (!(security_info_wanted & SECINFO_DACL)) {
+ psd->type &= ~SEC_DESC_DACL_PRESENT;
+ psd->dacl = NULL;
+ }
+ if (!(security_info_wanted & SECINFO_SACL)) {
+ psd->type &= ~SEC_DESC_SACL_PRESENT;
+ psd->sacl = NULL;
+ }
+
+ /* If the SACL/DACL is NULL, but was requested, we mark that it is
+ * present in the reply to match Windows behavior */
+ if (psd->sacl == NULL &&
+ security_info_wanted & SECINFO_SACL)
+ psd->type |= SEC_DESC_SACL_PRESENT;
+ if (psd->dacl == NULL &&
+ security_info_wanted & SECINFO_DACL)
+ psd->type |= SEC_DESC_DACL_PRESENT;
+
+ if (security_info_wanted & SECINFO_LABEL) {
+ /* Like W2K3 return a null object. */
+ psd->owner_sid = NULL;
+ psd->group_sid = NULL;
+ psd->dacl = NULL;
+ psd->sacl = NULL;
+ psd->type &= ~(SEC_DESC_DACL_PRESENT|SEC_DESC_SACL_PRESENT);
+ }
+
+ *ppsd = psd;
+ return NT_STATUS_OK;
+}
+
+/****************************************************************************
+ Write a security descriptor into marshalled format.
+****************************************************************************/
+
+static NTSTATUS smbd_marshall_security_desc(TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ struct security_descriptor *psd,
+ uint32_t max_data_count,
+ uint8_t **ppmarshalled_sd,
+ size_t *psd_size)
+{
+ *psd_size = ndr_size_security_descriptor(psd, 0);
+
+ DBG_NOTICE("sd_size = %zu.\n", *psd_size);
+
+ if (DEBUGLEVEL >= 10) {
+ DBG_DEBUG("security desc for file %s\n",
+ fsp_str_dbg(fsp));
+ NDR_PRINT_DEBUG(security_descriptor, psd);
+ }
+
+ if (max_data_count < *psd_size) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return marshall_sec_desc(mem_ctx,
+ psd,
+ ppmarshalled_sd,
+ psd_size);
+}
+
+/****************************************************************************
+ Reply to query a security descriptor.
+ Callable from SMB1 and SMB2.
+ If it returns NT_STATUS_BUFFER_TOO_SMALL, psd_size is initialized with
+ the required size.
+****************************************************************************/
+
+NTSTATUS smbd_do_query_security_desc(connection_struct *conn,
+ TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ uint32_t security_info_wanted,
+ uint32_t max_data_count,
+ uint8_t **ppmarshalled_sd,
+ size_t *psd_size)
+{
+ NTSTATUS status;
+ struct security_descriptor *psd = NULL;
+
+ /*
+ * Get the permissions to return.
+ */
+
+ status = smbd_fetch_security_desc(conn,
+ mem_ctx,
+ fsp,
+ security_info_wanted,
+ &psd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = smbd_marshall_security_desc(mem_ctx,
+ fsp,
+ psd,
+ max_data_count,
+ ppmarshalled_sd,
+ psd_size);
+ TALLOC_FREE(psd);
+ return status;
+}
+
+#ifdef HAVE_SYS_QUOTAS
+static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx,
+ struct files_struct *fsp,
+ SMB_NTQUOTA_HANDLE *qt_handle,
+ struct dom_sid *sids,
+ uint32_t elems)
+{
+ uint32_t i;
+ TALLOC_CTX *list_ctx = NULL;
+
+ list_ctx = talloc_init("quota_sid_list");
+
+ if (list_ctx == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
+ }
+
+ if (qt_handle->quota_list!=NULL) {
+ free_ntquota_list(&(qt_handle->quota_list));
+ }
+ for (i = 0; i < elems; i++) {
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *list_item;
+ bool ok;
+
+ if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp,
+ SMB_USER_QUOTA_TYPE,
+ &sids[i], &qt))) {
+ /* non fatal error, return empty item in result */
+ ZERO_STRUCT(qt);
+ continue;
+ }
+
+
+ list_item = talloc_zero(list_ctx, SMB_NTQUOTA_LIST);
+ if (list_item == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
+ }
+
+ ok = sid_to_uid(&sids[i], &list_item->uid);
+ if (!ok) {
+ struct dom_sid_buf buf;
+ DBG_WARNING("Could not convert SID %s to uid\n",
+ dom_sid_str_buf(&sids[i], &buf));
+ /* No idea what to return here... */
+ return NDR_ERR_INVALID_POINTER;
+ }
+
+ list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT);
+ if (list_item->quotas == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
+ }
+
+ *list_item->quotas = qt;
+ list_item->mem_ctx = list_ctx;
+ DLIST_ADD(qt_handle->quota_list, list_item);
+ }
+ qt_handle->tmp_list = qt_handle->quota_list;
+ return NDR_ERR_SUCCESS;
+}
+
+static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx,
+ uint32_t sidlistlength,
+ DATA_BLOB *sid_buf,
+ struct dom_sid **sids,
+ uint32_t *num)
+{
+ DATA_BLOB blob;
+ uint32_t i = 0;
+ enum ndr_err_code err;
+
+ struct sid_list_elem {
+ struct sid_list_elem *prev, *next;
+ struct dom_sid sid;
+ };
+
+ struct sid_list_elem *sid_list = NULL;
+ struct sid_list_elem *iter = NULL;
+ TALLOC_CTX *list_ctx = talloc_init("sid_list");
+ if (!list_ctx) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
+
+ *num = 0;
+ *sids = NULL;
+
+ if (sidlistlength) {
+ uint32_t offset = 0;
+ struct ndr_pull *ndr_pull = NULL;
+
+ if (sidlistlength > sid_buf->length) {
+ DBG_ERR("sid_list_length 0x%x exceeds "
+ "available bytes %zx\n",
+ sidlistlength,
+ sid_buf->length);
+ err = NDR_ERR_OFFSET;
+ goto done;
+ }
+ while (true) {
+ struct file_get_quota_info info;
+ struct sid_list_elem *item = NULL;
+ uint32_t new_offset = 0;
+ blob.data = sid_buf->data + offset;
+ blob.length = sidlistlength - offset;
+ ndr_pull = ndr_pull_init_blob(&blob, list_ctx);
+ if (!ndr_pull) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
+ err = ndr_pull_file_get_quota_info(ndr_pull,
+ NDR_SCALARS | NDR_BUFFERS, &info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_ERR("Failed to pull file_get_quota_info "
+ "from sidlist buffer\n");
+ goto done;
+ }
+ item = talloc_zero(list_ctx, struct sid_list_elem);
+ if (!item) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
+ item->sid = info.sid;
+ DLIST_ADD(sid_list, item);
+ i++;
+ if (i == UINT32_MAX) {
+ DBG_ERR("Integer overflow\n");
+ err = NDR_ERR_ARRAY_SIZE;
+ goto done;
+ }
+ new_offset = info.next_entry_offset;
+
+ /* if new_offset == 0 no more sid(s) to read. */
+ if (new_offset == 0) {
+ break;
+ }
+
+ /* Integer wrap? */
+ if ((offset + new_offset) < offset) {
+ DBG_ERR("Integer wrap while adding "
+ "new_offset 0x%x to current "
+ "buffer offset 0x%x\n",
+ new_offset, offset);
+ err = NDR_ERR_OFFSET;
+ goto done;
+ }
+
+ offset += new_offset;
+
+ /* check if new offset is outside buffer boundary. */
+ if (offset >= sidlistlength) {
+ DBG_ERR("bufsize 0x%x exceeded by "
+ "new offset 0x%x)\n",
+ sidlistlength,
+ offset);
+ err = NDR_ERR_OFFSET;
+ goto done;
+ }
+ }
+ *sids = talloc_zero_array(mem_ctx, struct dom_sid, i);
+ if (*sids == NULL) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
+
+ *num = i;
+
+ for (iter = sid_list, i = 0; iter; iter = iter->next, i++) {
+ struct dom_sid_buf buf;
+ (*sids)[i] = iter->sid;
+ DBG_DEBUG("quota SID[%u] %s\n",
+ (unsigned int)i,
+ dom_sid_str_buf(&iter->sid, &buf));
+ }
+ }
+ err = NDR_ERR_SUCCESS;
+done:
+ TALLOC_FREE(list_ctx);
+ return err;
+}
+
+NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ bool restart_scan,
+ bool return_single,
+ uint32_t sid_list_length,
+ DATA_BLOB *sid_buf,
+ uint32_t max_data_count,
+ uint8_t **p_data,
+ uint32_t *p_data_size)
+{
+ NTSTATUS status;
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ SMB_NTQUOTA_LIST *qt_list = NULL;
+ DATA_BLOB blob = data_blob_null;
+ enum ndr_err_code err;
+
+ qt_handle =
+ (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
+
+ if (sid_list_length ) {
+ struct dom_sid *sids;
+ uint32_t elems = 0;
+ /*
+ * error check pulled offsets and lengths for wrap and
+ * exceeding available bytes.
+ */
+ if (sid_list_length > sid_buf->length) {
+ DBG_ERR("sid_list_length 0x%x exceeds "
+ "available bytes %zx\n",
+ sid_list_length,
+ sid_buf->length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ err = extract_sids_from_buf(mem_ctx, sid_list_length,
+ sid_buf, &sids, &elems);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ err = fill_qtlist_from_sids(mem_ctx,
+ fsp,
+ qt_handle,
+ sids,
+ elems);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else if (restart_scan) {
+ if (vfs_get_user_ntquota_list(fsp,
+ &(qt_handle->quota_list))!=0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (qt_handle->quota_list!=NULL &&
+ qt_handle->tmp_list==NULL) {
+ free_ntquota_list(&(qt_handle->quota_list));
+ }
+ }
+
+ if (restart_scan !=0 ) {
+ qt_list = qt_handle->quota_list;
+ } else {
+ qt_list = qt_handle->tmp_list;
+ }
+ status = fill_quota_buffer(mem_ctx, qt_list,
+ return_single != 0,
+ max_data_count,
+ &blob,
+ &qt_handle->tmp_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (blob.length > max_data_count) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+ *p_data = blob.data;
+ *p_data_size = blob.length;
+ return NT_STATUS_OK;
+}
+#endif /* HAVE_SYS_QUOTAS */