1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-24 21:34:56 +03:00
samba-mirror/source3/smbd/smb2_nttrans.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

836 lines
22 KiB
C
Raw Normal View History

/*
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) {
if (!(fsp->access_mask & SEC_STD_WRITE_OWNER)) {
return NT_STATUS_ACCESS_DENIED;
}
}
if (security_info_sent & SECINFO_GROUP) {
if (!(fsp->access_mask & SEC_STD_WRITE_OWNER)) {
return NT_STATUS_ACCESS_DENIED;
}
}
if (security_info_sent & SECINFO_DACL) {
if (!(fsp->access_mask & SEC_STD_WRITE_DAC)) {
return NT_STATUS_ACCESS_DENIED;
}
/* Convert all the generic bits. */
if (psd->dacl) {
security_acl_map_generic(psd->dacl, &file_generic_mapping);
}
}
if (security_info_sent & SECINFO_SACL) {
if (!(fsp->access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
return NT_STATUS_ACCESS_DENIED;
}
/*
* Setting a SACL also requires WRITE_DAC.
* See the smbtorture3 SMB2-SACL test.
*/
if (!(fsp->access_mask & SEC_STD_WRITE_DAC)) {
return NT_STATUS_ACCESS_DENIED;
}
/* 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;
}
/****************************************************************************
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;
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;
}
return set_sd(fsp, psd, security_info_sent);
}
/****************************************************************************
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) &&
!(fsp->access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
DEBUG(10, ("Access to SACL denied.\n"));
return NT_STATUS_ACCESS_DENIED;
}
if ((security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|SECINFO_GROUP)) &&
!(fsp->access_mask & SEC_STD_READ_CONTROL)) {
DEBUG(10, ("Access to DACL, OWNER, or GROUP denied.\n"));
return NT_STATUS_ACCESS_DENIED;
}
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 securty 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 boundry. */
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 */