RFC smbd smb2 quota support (+ patches to get rid of manual marshall/unmarshall by ndr-ising the existing code)

Noel Power nopower at suse.com
Fri Mar 3 09:56:48 UTC 2017


Hi Jeremy,

On 02/03/17 22:05, Jeremy Allison wrote:
> On Thu, Mar 02, 2017 at 04:26:39PM +0000, Noel Power wrote:
>> On 02/03/17 13:59, Noel Power wrote:
>>> On 02/03/17 12:09, Noel Power wrote:
>>>> Hi,
>>>>
>>>> I got curious after failing to get any useful response from the windows
>>>> quota dialog tab (win8.1 client) when connected to a samba share. Seems
>>>> smbd doesn't support some of the quota related messages in smb2. The
>>>> attached patches hope to fix that, additionally I tried to clean up some
>>>> of the code by removing some of the manual marshall/unmarshall code.
>>>>
>>>> The last patch in the series (PATCH 9) modifies build_user_quota_buffer,
>>>> the resulting function is quite similar to the fill_quota_buffer from
>>>> PATCH 4, the implementation could be made common for the client & smbd I
>>>> think (and probably live in libsmb somewhere) I'd be interested to hear
>>>> if that would be the correct place to put it (or even any other
>>>> suggestions). If it is ok I would like to do that as a followup patch
>>>>
>>>> Noel
[...]
> In [PATCH 4/9] s3/smbd: Add support for GetInfo/SMB2_0_INFO_QUOTA
> [MS-SMB2] 2.2.37.1 requests
> You have:
[...]
> +                       item->sid = info.sid;
> +                       DLIST_ADD(sid_list, item);
> +                       i++;
> +                       offset = info.next_entry_offset;
> +                       can_pull = (offset > 0);
> +               }
>
> Don't use 'int i' here. You're parsing stuff
> off the wire and MUST be paranoid. I suggest
> uint32_t i, and add a range/wrap check after
> the i++.
>
> I know wrap here is essentially impossible,
> but I'm trying to teach everyone to be scared
'i' should in anycase be unsigned thanks for catching that. 'i' isn't
really used in the parsing though (I depend completely on the ndr
routines) it's just a counter. Agreed you can never be paranoid enough
(I am already paranoid about my ability to screw things up :-)) so
hopefully the new patch does the needed :-)

Noel
-------------- next part --------------
>From bbf1634585435b89537e40f33d13fcc74a7649a2 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 28 Feb 2017 15:04:16 +0000
Subject: [PATCH 1/9] s3/libsmb: Avoid potential smbpanic calling
 parse_user_quota_list.

Calling parse_user_quota_list with a NULL buffer can cause a panic, while
this shouldn't happen, I managed to trigger this with an early implementation
of SMB2 quota support in smbd which didn't pass back NT_STATUS_NO_MORE_ENTRIES
when handling a SMB2_0_INFO_QUOTA GETINFO message.
OTHOH the Windows client handled the same situation gracefully.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/libsmb/cli_smb2_fnum.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 848e077..7977bea 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2532,6 +2532,14 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
 				    ph->fid_persistent, ph->fid_volatile, frame,
 				    &outbuf);
 
+	/*
+	 * safeguard against panic from calling parse_user_quota_list with
+	 * NULL buffer
+	 */
+	if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
+		status = NT_STATUS_NO_MORE_ENTRIES;
+	}
+
 	if (!NT_STATUS_IS_OK(status)) {
 		goto cleanup;
 	}
-- 
2.10.2


>From 8ea066802c5935ac3bb6b0ee361a3e1ef8d22cda Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 28 Feb 2017 11:36:47 +0000
Subject: [PATCH 2/9] s3/smbd: Don't stat when doing a quota operation (as it's
 a fake file)

calling SMB_VFS_STAT on the quota fake file fails and caused
FS_INFO/FileFsControlInfo request to error out early, in turn stopped a
Win8.1 client from proceeding with quota queries.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/trans2.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index f58aacf..6de7795 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -3435,7 +3435,8 @@ NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn,
 	ZERO_STRUCT(smb_fname);
 	smb_fname.base_name = discard_const_p(char, filename);
 
-	if(SMB_VFS_STAT(conn, &smb_fname) != 0) {
+	if(info_level != SMB_FS_QUOTA_INFORMATION
+	   && SMB_VFS_STAT(conn, &smb_fname) != 0) {
 		DEBUG(2,("stat of . failed (%s)\n", strerror(errno)));
 		return map_nt_error_from_unix(errno);
 	}
-- 
2.10.2


>From 0b14c103ae8bb6d2c0f534645eed6bf3fed421b9 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 2 Mar 2017 09:20:24 +0000
Subject: [PATCH 3/9] librpc/idl Add some query [getset]info quota related
 structures

---
 librpc/idl/quota.idl     | 55 ++++++++++++++++++++++++++++++++++++++++++++++++
 librpc/idl/wscript_build |  1 +
 librpc/wscript_build     |  5 +++++
 3 files changed, 61 insertions(+)
 create mode 100644 librpc/idl/quota.idl

diff --git a/librpc/idl/quota.idl b/librpc/idl/quota.idl
new file mode 100644
index 0000000..6ac747f
--- /dev/null
+++ b/librpc/idl/quota.idl
@@ -0,0 +1,55 @@
+#include "idl_types.h"
+
+import "security.idl";
+
+[
+	pointer_default(unique)
+]
+
+interface file_quota {
+
+	/* MS-FSCC 2.4.33.1 */
+	typedef [public] struct {
+		uint32 next_entry_offset;
+		uint32 sid_length;
+		dom_sid sid;
+	} file_get_quota_info;
+
+	/* MS-FSCC 2.4.33 */
+	typedef [public] struct {
+		uint32 next_entry_offset;
+		uint32 sid_length;
+		hyper change_time;
+		hyper quota_used;
+		hyper quota_threshold;
+		hyper quota_limit;
+		dom_sid sid;
+	} file_quota_information;
+}
+
+interface smb2_query_quoata
+{
+	/* MS-SMB2 2.2.37.1 */
+	typedef [public] struct {
+		uint8 return_single;
+		uint8 restart_scan;
+		uint16 reserved;
+		uint32 sid_list_length;
+		uint32 start_sid_length;
+		uint32 start_sid_offset;
+		uint8 sid_buf[sid_list_length ? sid_list_length : start_sid_length + start_sid_offset];
+	} smb2_query_quota_info;
+}
+
+interface smb1_nt_transact_query_quota
+{
+	/* MS-SMB 2.2.7.5.1 */
+	typedef [public] struct {
+		uint16 fid;
+		uint8 return_single_entry;
+		uint8 restart_scan;
+		uint32 sid_list_length;
+		uint32 start_sid_length;
+		uint32 start_sid_offset;
+	} nttrans_query_quota_params;
+}
diff --git a/librpc/idl/wscript_build b/librpc/idl/wscript_build
index 1f09ae0..346ae78 100644
--- a/librpc/idl/wscript_build
+++ b/librpc/idl/wscript_build
@@ -12,6 +12,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
                        drsblobs.idl efs.idl frstrans.idl mgmt.idl netlogon.idl
                        notify.idl
                        smb2_lease_struct.idl
+                       quota.idl
                        policyagent.idl scerpc.idl svcctl.idl wkssvc.idl eventlog6.idl backupkey.idl
                        fsrvp.idl bkupblobs.idl fscc.idl frsblobs.idl witness.idl clusapi.idl
                        mdssvc.idl winspool.idl''',
diff --git a/librpc/wscript_build b/librpc/wscript_build
index fdfe641..e4af66c 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -399,6 +399,11 @@ bld.SAMBA_SUBSYSTEM('NDR_SMB2_LEASE_STRUCT',
     public_headers='gen_ndr/smb2_lease_struct.h'
     )
 
+bld.SAMBA_SUBSYSTEM('NDR_QUOTA',
+    source='gen_ndr/ndr_quota.c',
+    public_deps='ndr',
+    )
+
 bld.SAMBA_SUBSYSTEM('NDR_SCHANNEL',
     source='ndr/ndr_schannel.c gen_ndr/ndr_schannel.c',
     public_deps='ndr ndr_nbt'
-- 
2.10.2


>From bb7ccd603624888415016bbcebb641ef123eca73 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 21 Feb 2017 11:09:08 +0000
Subject: [PATCH 4/9] s3/smbd: Add support for GetInfo/SMB2_0_INFO_QUOTA
 [MS-SMB2] 2.2.37.1 requests

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/nttrans.c      | 321 ++++++++++++++++++++++++++++++++++++++++++++
 source3/smbd/proto.h        |  11 ++
 source3/smbd/smb2_getinfo.c |  49 ++++++-
 source3/wscript_build       |   1 +
 4 files changed, 379 insertions(+), 3 deletions(-)

diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index 5f122a9..f46f919 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -30,6 +30,8 @@
 #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;
 
@@ -2291,6 +2293,325 @@ static void call_nt_transact_ioctl(connection_struct *conn,
 
 
 #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)
+{
+	int 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;
+
+		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;
+		}
+
+		sid_to_uid(&sids[i], &list_item->uid);
+		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,
+				  uint32_t startsidlength,
+				  uint32_t startsidoffset,
+				  uint8_t *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;
+		bool can_pull = true;
+		struct ndr_pull *ndr_pull = NULL;
+
+		blob.data = sid_buf;
+		blob.length = sidlistlength;
+		ndr_pull = ndr_pull_init_blob(&blob, mem_ctx);
+		while (can_pull) {
+			struct file_get_quota_info info;
+			struct sid_list_elem *item = NULL;
+			NDR_PULL_NEED_BYTES(ndr_pull, offset);
+			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;
+			}
+			offset = info.next_entry_offset;
+			can_pull = (offset > 0);
+		}
+		*sids = talloc_zero_array(mem_ctx, struct dom_sid, i);
+		if (!sids) {
+			DBG_ERR("OOM\n");
+			err = NDR_ERR_ALLOC;
+			goto done;
+		}
+
+		*num = i;
+
+		for (iter = sid_list, i = 0; iter; iter = iter->next, i++) {
+			*sids[i] = iter->sid;
+			DBG_DEBUG("quota SID[%d] %s\n", i, sid_string_dbg(&iter->sid));
+		}
+	} else {
+		*sids = talloc_zero_array(mem_ctx, struct dom_sid, i);
+		if (!sids) {
+			DBG_ERR("OOM\n");
+			err = NDR_ERR_ALLOC;
+			goto done;
+		}
+		err = ndr_pull_struct_blob(&blob, mem_ctx, &sids[0],
+					(ndr_pull_flags_fn_t)ndr_pull_dom_sid);
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			DBG_ERR("Failed to pull sid from sid buffer\n");
+			goto done;
+		}
+	}
+
+	err = NDR_ERR_SUCCESS;
+done:
+	TALLOC_FREE(list_ctx);
+	return err;
+}
+
+static NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
+			      SMB_NTQUOTA_HANDLE *qt_handle,
+			      bool rescan,
+			      bool return_single,
+			      uint32_t max_data,
+			      uint8_t **data,
+			      uint32_t *datalen)
+{
+	SMB_NTQUOTA_STRUCT qt;
+	SMB_NTQUOTA_LIST *tmp_list;
+	int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+	struct ndr_push *header_ndr = ndr_push_init_ctx(mem_ctx);
+	uint32_t start_offset = 0;
+	uint32_t num_pushed = 0;
+	uint32_t next_align = 0;
+	bool max_data_exceeded = false;
+
+	ZERO_STRUCT(qt);
+	*data = NULL;
+	*datalen = 0;
+
+	if (rescan) {
+		tmp_list = qt_handle->quota_list;
+	} else {
+		tmp_list = qt_handle->tmp_list;
+	}
+
+	if (tmp_list == NULL) {
+		return NT_STATUS_NO_MORE_ENTRIES;
+	}
+	for (;tmp_list!=NULL; tmp_list=tmp_list->next, num_pushed++) {
+		struct file_quota_information info;
+		enum ndr_err_code err;
+		ZERO_STRUCT(info);
+		info.sid_length = ndr_size_dom_sid(&tmp_list->quotas->sid, 0);
+
+		if (((header_ndr->offset - next_align)
+		   + sizeof(info.next_entry_offset)
+		   + sizeof(info.sid_length)
+		   + sizeof(info.change_time)
+		   + sizeof(info.quota_used)
+		   + sizeof(info.quota_threshold)
+		   + sizeof(info.quota_limit)
+		   + info.sid_length) > max_data) {
+			max_data_exceeded = true;
+			break;
+		}
+		start_offset = header_ndr->offset;
+
+		info.sid = tmp_list->quotas->sid;
+		info.quota_used = tmp_list->quotas->usedspace;
+		info.quota_threshold = tmp_list->quotas->softlim;
+		info.quota_limit = tmp_list->quotas->hardlim;
+
+		err = ndr_push_file_quota_information(header_ndr,
+						      ndr_flags,
+						      &info);
+
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			DBG_DEBUG("Failed to pull the quota sid\n");
+			return NT_STATUS_INTERNAL_ERROR;
+		}
+
+		/* pidl will align to 8 bytes due to 8 byte members*/
+		next_align = header_ndr->offset;
+		ndr_push_align(header_ndr, 8);
+		next_align = header_ndr->offset - next_align;
+		info.next_entry_offset = header_ndr->offset - start_offset;
+
+		if (return_single) {
+			break;
+		}
+
+		/* write nextentryoffset to the buffer too */
+		SIVAL(header_ndr->data, start_offset, info.next_entry_offset);
+	}
+
+
+	qt_handle->tmp_list = tmp_list;
+
+	*datalen = header_ndr->offset - next_align;
+	*data = header_ndr->data;
+
+	if (num_pushed) {
+		/* terminate the list */
+		SIVAL(header_ndr->data, start_offset, 0);
+	}
+
+	/*
+	 * if we failed to write a single file_quota_information
+	 * issue BUFFER_TOO_SMALL
+	 */
+	if (max_data_exceeded && num_pushed < 1) {
+		return NT_STATUS_BUFFER_TOO_SMALL;
+	}
+
+	return NT_STATUS_OK;
+}
+
+NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
+				     files_struct *fsp,
+				     struct smb2_query_quota_info *info,
+				     uint32_t max_data_count,
+				     uint8_t **p_data,
+				     uint32_t *p_data_size)
+{
+	NTSTATUS status;
+	SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+	uint8_t *data = NULL;
+	uint32_t data_size = 0;
+	enum ndr_err_code err;
+
+	DBG_DEBUG("sidlistlength = %d, startsidlength = %d, "
+		  "startsidoffset = %d\n",
+		  info->sid_list_length,
+		  info->start_sid_length,
+		  info->start_sid_offset);
+
+	qt_handle =
+		(SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
+
+	if (info->sid_list_length || info->start_sid_length
+	    || info->start_sid_offset) {
+		struct dom_sid *sids;
+		uint32_t elems = 0;
+		err = extract_sids_from_buf(mem_ctx, info->sid_list_length,
+					    info->start_sid_length,
+					    info->start_sid_offset,
+					    info->sid_buf, &sids, &elems);
+		if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) {
+			status = NT_STATUS_INVALID_PARAMETER;
+			goto done;
+		}
+		err = fill_qtlist_from_sids(mem_ctx,
+					    fsp,
+					    qt_handle,
+					    sids,
+					    elems);
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			status = NT_STATUS_INVALID_PARAMETER;
+			goto done;
+		}
+	} else if (info->restart_scan) {
+		if (vfs_get_user_ntquota_list(fsp,
+					      &(qt_handle->quota_list))!=0) {
+			status = NT_STATUS_INTERNAL_ERROR;
+			goto done;
+		}
+	} else {
+		if (qt_handle->quota_list!=NULL &&
+			qt_handle->tmp_list==NULL) {
+			free_ntquota_list(&(qt_handle->quota_list));
+		}
+	}
+
+	status = fill_quota_buffer(mem_ctx, qt_handle,
+				   info->restart_scan !=0,
+				   info->return_single != 0,
+				   max_data_count,
+				   &data,
+				   &data_size);
+	*p_data = data;
+	*p_data_size = data_size;
+done:
+	return status;
+}
+
 /****************************************************************************
  Reply to get user quota
 ****************************************************************************/
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 7ccb12a..a1041a8 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -633,6 +633,17 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn,
 					uint32_t max_data_count,
 					uint8_t **ppmarshalled_sd,
 					size_t *psd_size);
+#ifdef HAVE_SYS_QUOTAS
+
+struct smb2_query_quota_info;
+
+NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
+				     files_struct *fsp,
+				     struct smb2_query_quota_info *info,
+				     uint32_t max_data_count,
+				     uint8_t **p_data,
+				     uint32_t *p_data_size);
+#endif
 void reply_nttrans(struct smb_request *req);
 void reply_nttranss(struct smb_request *req);
 
diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c
index 7f44868..7e44bad 100644
--- a/source3/smbd/smb2_getinfo.c
+++ b/source3/smbd/smb2_getinfo.c
@@ -25,6 +25,8 @@
 #include "../libcli/smb/smb_common.h"
 #include "trans2.h"
 #include "../lib/util/tevent_ntstatus.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_security.h"
 
 static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx,
 						 struct tevent_context *ev,
@@ -517,9 +519,50 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx,
 		break;
 	}
 
-	case SMB2_GETINFO_QUOTA:
-		tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
-		return tevent_req_post(req, ev);
+	case SMB2_GETINFO_QUOTA: {
+		struct smb2_query_quota_info info;
+		enum ndr_err_code err;
+		uint8_t *data = NULL;
+		uint32_t data_size = 0;	
+		TALLOC_CTX *tmp_ctx = talloc_init("geninfo_quota");
+
+		err = ndr_pull_struct_blob( &in_input_buffer, req, &info,
+			(ndr_pull_flags_fn_t)ndr_pull_smb2_query_quota_info);
+
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			DBG_DEBUG("failed to pull smb2_query_quota_info\n");
+			tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+			return tevent_req_post(req, ev);
+		}
+		DBG_DEBUG("quota list returnsingle %d, restartscan %d, "
+			  "sid_list_length %d, start_sid_length %d, "
+			  "startsidoffset %d info.sid_buf %p\n",
+			  info.return_single,
+			  info.restart_scan,
+			  info.sid_list_length,
+			  info.start_sid_length,
+			  info.start_sid_offset,
+			  info.sid_buf);
+
+		status = smbd_do_query_getinfo_quota(tmp_ctx,
+				  fsp,
+				  &info,
+				  in_output_buffer_length,
+				  &data,
+				  &data_size);
+
+		if (!NT_STATUS_IS_OK(status)) {
+			TALLOC_FREE(tmp_ctx);
+			tevent_req_nterror(req, status);
+			return tevent_req_post(req, ev);
+		}
+
+		state->out_output_buffer =
+			data_blob_talloc(state, data, data_size);
+		status  = NT_STATUS_OK;
+		TALLOC_FREE(tmp_ctx);
+		break;
+	}
 
 	default:
 		DEBUG(10,("smbd_smb2_getinfo_send: "
diff --git a/source3/wscript_build b/source3/wscript_build
index 8c9a15b..3414761 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -753,6 +753,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
                         netapi
                         NDR_IOCTL
                         notifyd
+                        NDR_QUOTA
                    ''' +
                    bld.env['dmapi_lib'] +
                    bld.env['legacy_quota_libs'] +
-- 
2.10.2


>From 3b06d8a5ffca2b81d7a9ed9fd9d47ac985d9f5ae Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 1 Mar 2017 18:58:21 +0000
Subject: [PATCH 5/9] s3/smbd: Add support for SetInfo/SMB2_0_INFO_QUOTA
 [MS-SMB2] 2.2.39

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/smb2_setinfo.c | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c
index db00ba0..21c8746 100644
--- a/source3/smbd/smb2_setinfo.c
+++ b/source3/smbd/smb2_setinfo.c
@@ -28,6 +28,7 @@
 #include "../librpc/gen_ndr/open_files.h"
 #include "source3/lib/dbwrap/dbwrap_watch.h"
 #include "messages.h"
+#include "librpc/gen_ndr/ndr_quota.h"
 
 static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
 						 struct tevent_context *ev,
@@ -567,6 +568,37 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
 		break;
 	}
 
+	case 0x04:/* SMB2_SETINFO_QUOTA */
+	{
+		struct file_quota_information info = {0};
+		SMB_NTQUOTA_STRUCT qt = {0};
+		enum ndr_err_code err;
+		if (!fsp->fake_file_handle) {
+			tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+			return tevent_req_post(req, ev);
+		}
+		err = ndr_pull_struct_blob(
+				&in_input_buffer, state, &info,
+				(ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+			return tevent_req_post(req, ev);
+		}
+
+		qt.usedspace = info.quota_used;
+
+		qt.softlim = info.quota_threshold;
+
+		qt.hardlim = info.quota_limit;
+
+		qt.sid = info.sid;
+		if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt)!=0) {
+			tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+			return tevent_req_post(req, ev);
+		}
+		status = NT_STATUS_OK;
+		break;
+	}
 	default:
 		tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
 		return tevent_req_post(req, ev);
-- 
2.10.2


>From da0a51b03cb5a3ee02af4ba735d0f469614e5ce5 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 1 Mar 2017 18:22:48 +0000
Subject: [PATCH 6/9] s3/smbd: Use ndr generated structures and routines for
 SMB1 SET_QUOTA msg

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/nttrans.c | 82 +++++++++++++++++++++++---------------------------
 1 file changed, 38 insertions(+), 44 deletions(-)

diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index f46f919..bea821e 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -2910,10 +2910,13 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
 	char *pdata = *ppdata;
 	int data_len=0,param_len=0;
 	SMB_NTQUOTA_STRUCT qt;
-	size_t sid_len;
+	struct file_quota_information info = {0};
+	enum ndr_err_code err;
 	struct dom_sid sid;
+	DATA_BLOB inblob;
 	files_struct *fsp = NULL;
-
+	TALLOC_CTX *ctx = NULL;
+	NTSTATUS status = NT_STATUS_OK;
 	ZERO_STRUCT(qt);
 
 	/* access check */
@@ -2921,8 +2924,8 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
 		DEBUG(1,("set_user_quota: access_denied service [%s] user "
 			 "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
 			 conn->session_info->unix_info->unix_name));
-		reply_nterror(req, NT_STATUS_ACCESS_DENIED);
-		return;
+		status = NT_STATUS_ACCESS_DENIED;
+		goto error;
 	}
 
 	/*
@@ -2931,67 +2934,58 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
 
 	if (parameter_count < 2) {
 		DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= 2 bytes parameters\n",parameter_count));
-		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto error;
 	}
 
 	/* maybe we can check the quota_fnum */
 	fsp = file_fsp(req, SVAL(params,0));
 	if (!check_fsp_ntquota_handle(conn, req, fsp)) {
 		DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
-		reply_nterror(req, NT_STATUS_INVALID_HANDLE);
-		return;
+		status = NT_STATUS_INVALID_HANDLE;
+		goto error;
 	}
 
-	if (data_count < 40) {
-		DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %d bytes data\n",data_count,40));
-		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
+	ctx = talloc_init("set_user_quota");
+	if (!ctx) {
+		status = NT_STATUS_NO_MEMORY;
+		goto error;
 	}
+	inblob.data = (uint8_t*)pdata;
+	inblob.length = data_count;
 
-	/* offset to next quota record.
-	 * 4 bytes IVAL(pdata,0)
-	 * unused here...
-	 */
+	err = ndr_pull_struct_blob(
+			&inblob,
+			ctx,
+			&info,
+			(ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
 
-	/* sid len */
-	sid_len = IVAL(pdata,4);
-
-	if (data_count < 40+sid_len || (40+sid_len < sid_len)) {
-		DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %lu bytes data\n",data_count,(unsigned long)40+sid_len));
-		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		DEBUG(0,("TRANSACT_SET_USER_QUOTA: failed to pull "
+			 "file_quota_information\n"));
+		status = NT_STATUS_INVALID_PARAMETER;
+		goto error;
 	}
+	qt.usedspace = info.quota_used;
 
-	/* unknown 8 bytes in pdata
-	 * maybe its the change time in NTTIME
-	 */
+	qt.softlim = info.quota_threshold;
 
-	/* the used space 8 bytes (uint64_t)*/
-	qt.usedspace = BVAL(pdata,16);
+	qt.hardlim = info.quota_limit;
 
-	/* the soft quotas 8 bytes (uint64_t)*/
-	qt.softlim = BVAL(pdata,24);
-
-	/* the hard quotas 8 bytes (uint64_t)*/
-	qt.hardlim = BVAL(pdata,32);
-
-	if (!sid_parse((const uint8_t *)(pdata+40), sid_len, &sid)) {
-		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
-	}
-
-	DEBUGADD(8,("SID: %s\n", sid_string_dbg(&sid)));
-
-	/* 44 unknown bytes left... */
+	sid = info.sid;
 
 	if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
-		reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
-		return;
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto error;
 	}
 
 	send_nt_replies(conn, req, NT_STATUS_OK, params, param_len,
 			pdata, data_len);
+	TALLOC_FREE(ctx);
+	return;
+error:
+	TALLOC_FREE(ctx);
+	reply_nterror(req, status);
 }
 #endif /* HAVE_SYS_QUOTAS */
 
-- 
2.10.2


>From 39c773984e86c4408ae441291cdd7f52429a5aa9 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 24 Feb 2017 14:28:39 +0000
Subject: [PATCH 7/9] s3/smbd: Leverage the new common code
 smbd_do_query_getinfo_quota.

Make both SMB1 & SMB2 use common code for quota queries.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/smbd/nttrans.c | 329 ++++++++++++++-----------------------------------
 1 file changed, 94 insertions(+), 235 deletions(-)

diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index bea821e..4a563f8 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -2616,6 +2616,30 @@ done:
  Reply to get user quota
 ****************************************************************************/
 
+/*
+ * convert nttrans_query_quota_params values to conform to smb2 expectations
+ * and return struct smb2_query_quota_info to use in
+ * smbd_do_query_getinfo_quota
+ */
+static void convert_quoata_trans_param(
+		struct nttrans_query_quota_params *in,
+		uint8_t *pdata,
+		struct smb2_query_quota_info *out_smb2_info)
+{
+	ZERO_STRUCTP(out_smb2_info);
+	out_smb2_info->return_single = in->return_single_entry;
+	out_smb2_info->restart_scan = in->restart_scan;
+	out_smb2_info->sid_buf = pdata;
+	if (in->sid_list_length) {
+		out_smb2_info->sid_list_length = in->sid_list_length;
+		out_smb2_info->start_sid_offset = 0;
+		out_smb2_info->start_sid_length = 0;
+	} else if (in->start_sid_length) {
+		out_smb2_info->sid_list_length = in->start_sid_length;
+		out_smb2_info->sid_buf += in->start_sid_offset;
+	}
+}
+
 static void call_nt_transact_get_user_quota(connection_struct *conn,
 					    struct smb_request *req,
 					    uint16_t **ppsetup,
@@ -2629,267 +2653,102 @@ static void call_nt_transact_get_user_quota(connection_struct *conn,
 	NTSTATUS nt_status = NT_STATUS_OK;
 	char *params = *ppparams;
 	char *pdata = *ppdata;
-	char *entry;
 	int data_len=0,param_len=0;
-	int qt_len=0;
-	int entry_len = 0;
 	files_struct *fsp = NULL;
-	uint16_t level = 0;
-	size_t sid_len;
-	struct dom_sid sid;
-	bool start_enum = True;
 	SMB_NTQUOTA_STRUCT qt;
-	SMB_NTQUOTA_LIST *tmp_list;
-	SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+	DATA_BLOB blob;
+	struct nttrans_query_quota_params info;
+	enum ndr_err_code err;
+	TALLOC_CTX *tmp_ctx = NULL;
+	uint32_t resp_len = 0;
+	uint8_t *resp_data = 0;
+	struct smb2_query_quota_info info_smb2;
 
 	ZERO_STRUCT(qt);
-
+	ZERO_STRUCT(info);
+	tmp_ctx = talloc_init("ntquota_list");
+	if (!tmp_ctx) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto error;
+	}
 	/* access check */
 	if (get_current_uid(conn) != sec_initial_uid()) {
 		DEBUG(1,("get_user_quota: access_denied service [%s] user "
 			 "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
 			 conn->session_info->unix_info->unix_name));
-		reply_nterror(req, NT_STATUS_ACCESS_DENIED);
-		return;
+		nt_status = NT_STATUS_ACCESS_DENIED;
+		goto error;
 	}
 
-	/*
-	 * Ensure minimum number of parameters sent.
-	 */
+	blob.data = (uint8_t*)params;
+	blob.length = parameter_count;
+	err = ndr_pull_struct_blob(&blob, tmp_ctx, &info,
+		(ndr_pull_flags_fn_t)ndr_pull_nttrans_query_quota_params);
 
-	if (parameter_count < 4) {
-		DEBUG(0,("TRANSACT_GET_USER_QUOTA: requires %d >= 4 bytes parameters\n",parameter_count));
-		reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-		return;
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		DEBUG(0,("TRANSACT_GET_USER_QUOTA: failed to pull "
+			 "query_quota_params."));
+		nt_status = NT_STATUS_INVALID_PARAMETER;
+		goto error;
 	}
+	DBG_DEBUG("info.return_single_entry = %d, info.restart_scan = %d, "
+		  "info.sid_list_length = %d, info.start_sid_length = %d, "
+		  "info.start_sid_offset = %d\n",
+		  info.return_single_entry,
+		  info.restart_scan,
+		  info.sid_list_length,
+		  info.start_sid_length,
+		  info.start_sid_offset);
+
+	/* populate smb2 equivelant structure */
+	convert_quoata_trans_param(&info, (uint8_t*)pdata, &info_smb2);
 
 	/* maybe we can check the quota_fnum */
-	fsp = file_fsp(req, SVAL(params,0));
+	fsp = file_fsp(req, info.fid);
 	if (!check_fsp_ntquota_handle(conn, req, fsp)) {
 		DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
-		reply_nterror(req, NT_STATUS_INVALID_HANDLE);
-		return;
+		nt_status = NT_STATUS_INVALID_HANDLE;
+		goto error;
+	}
+	nt_status = smbd_do_query_getinfo_quota(tmp_ctx,
+				  fsp,
+				  &info_smb2,
+				  max_data_count,
+				  &resp_data,
+				  &resp_len);
+	if (!NT_STATUS_IS_OK(nt_status)) {
+		/*
+		 * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
+		 * this status.
+		 */
+		if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
+			nt_status = NT_STATUS_OK;
+		} else {
+			goto error;
+		}
 	}
 
-	/* the NULL pointer checking for fsp->fake_file_handle->pd
-	 * is done by CHECK_NTQUOTA_HANDLE_OK()
-	 */
-	qt_handle = (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
-
-	level = SVAL(params,2);
-
-	/* unknown 12 bytes leading in params */
-
-	switch (level) {
-		case TRANSACT_GET_USER_QUOTA_LIST_CONTINUE:
-			/* seems that we should continue with the enum here --metze */
-
-			if (qt_handle->quota_list!=NULL &&
-			    qt_handle->tmp_list==NULL) {
-
-				/* free the list */
-				free_ntquota_list(&(qt_handle->quota_list));
-
-				/* Realloc the size of parameters and data we will return */
-				param_len = 4;
-				params = nttrans_realloc(ppparams, param_len);
-				if(params == NULL) {
-					reply_nterror(req, NT_STATUS_NO_MEMORY);
-					return;
-				}
-
-				data_len = 0;
-				SIVAL(params,0,data_len);
-
-				break;
-			}
-
-			start_enum = False;
-
-		case TRANSACT_GET_USER_QUOTA_LIST_START:
-
-			if (qt_handle->quota_list==NULL &&
-				qt_handle->tmp_list==NULL) {
-				start_enum = True;
-			}
-
-			if (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0) {
-				reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
-				return;
-			}
-
-			/* Realloc the size of parameters and data we will return */
-			param_len = 4;
-			params = nttrans_realloc(ppparams, param_len);
-			if(params == NULL) {
-				reply_nterror(req, NT_STATUS_NO_MEMORY);
-				return;
-			}
-
-			/* we should not trust the value in max_data_count*/
-			max_data_count = MIN(max_data_count,2048);
-
-			pdata = nttrans_realloc(ppdata, max_data_count);/* should be max data count from client*/
-			if(pdata == NULL) {
-				reply_nterror(req, NT_STATUS_NO_MEMORY);
-				return;
-			}
-
-			entry = pdata;
-
-			/* set params Size of returned Quota Data 4 bytes*/
-			/* but set it later when we know it */
-
-			/* for each entry push the data */
-
-			if (start_enum) {
-				qt_handle->tmp_list = qt_handle->quota_list;
-			}
-
-			tmp_list = qt_handle->tmp_list;
-
-			for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)<max_data_count));
-				tmp_list=tmp_list->next,entry+=entry_len,qt_len+=entry_len) {
-
-				sid_len = ndr_size_dom_sid(
-					&tmp_list->quotas->sid, 0);
-				entry_len = 40 + sid_len;
-
-				/* nextoffset entry 4 bytes */
-				SIVAL(entry,0,entry_len);
-
-				/* then the len of the SID 4 bytes */
-				SIVAL(entry,4,sid_len);
-
-				/* unknown data 8 bytes uint64_t */
-				SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-metze*/
-
-				/* the used disk space 8 bytes uint64_t */
-				SBIG_UINT(entry,16,tmp_list->quotas->usedspace);
-
-				/* the soft quotas 8 bytes uint64_t */
-				SBIG_UINT(entry,24,tmp_list->quotas->softlim);
-
-				/* the hard quotas 8 bytes uint64_t */
-				SBIG_UINT(entry,32,tmp_list->quotas->hardlim);
-
-				/* and now the SID */
-				sid_linearize((uint8_t *)(entry+40), sid_len,
-					      &tmp_list->quotas->sid);
-			}
-
-			qt_handle->tmp_list = tmp_list;
-
-			/* overwrite the offset of the last entry */
-			SIVAL(entry-entry_len,0,0);
-
-			data_len = 4+qt_len;
-			/* overwrite the params quota_data_len */
-			SIVAL(params,0,data_len);
-
-			break;
-
-		case TRANSACT_GET_USER_QUOTA_FOR_SID:
-
-			/* unknown 4 bytes IVAL(pdata,0) */
-
-			if (data_count < 8) {
-				DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %d bytes data\n",data_count,8));
-				reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-				return;
-			}
-
-			sid_len = IVAL(pdata,4);
-			/* Ensure this is less than 1mb. */
-			if (sid_len > (1024*1024)) {
-				reply_nterror(req, NT_STATUS_NO_MEMORY);
-				return;
-			}
-
-			if (data_count < 8+sid_len) {
-				DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %lu bytes data\n",data_count,(unsigned long)(8+sid_len)));
-				reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-				return;
-			}
-
-			data_len = 4+40+sid_len;
-
-			if (max_data_count < data_len) {
-				DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: max_data_count(%d) < data_len(%d)\n",
-					max_data_count, data_len));
-				param_len = 4;
-				SIVAL(params,0,data_len);
-				data_len = 0;
-				nt_status = NT_STATUS_BUFFER_TOO_SMALL;
-				break;
-			}
-
-			if (!sid_parse((const uint8_t *)(pdata+8), sid_len,
-				       &sid)) {
-				reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
-				return;
-			}
-
-			nt_status = vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE,
-						    &sid, &qt);
-			if (!NT_STATUS_IS_OK(nt_status)) {
-				reply_nterror(req, nt_status);
-				return;
-			}
-
-			/* Realloc the size of parameters and data we will return */
-			param_len = 4;
-			params = nttrans_realloc(ppparams, param_len);
-			if(params == NULL) {
-				reply_nterror(req, NT_STATUS_NO_MEMORY);
-				return;
-			}
-
-			pdata = nttrans_realloc(ppdata, data_len);
-			if(pdata == NULL) {
-				reply_nterror(req, NT_STATUS_NO_MEMORY);
-				return;
-			}
-
-			entry = pdata;
-
-			/* set params Size of returned Quota Data 4 bytes*/
-			SIVAL(params,0,data_len);
-
-			/* nextoffset entry 4 bytes */
-			SIVAL(entry,0,0);
-
-			/* then the len of the SID 4 bytes */
-			SIVAL(entry,4,sid_len);
-
-			/* unknown data 8 bytes uint64_t */
-			SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-mezte*/
-
-			/* the used disk space 8 bytes uint64_t */
-			SBIG_UINT(entry,16,qt.usedspace);
-
-			/* the soft quotas 8 bytes uint64_t */
-			SBIG_UINT(entry,24,qt.softlim);
-
-			/* the hard quotas 8 bytes uint64_t */
-			SBIG_UINT(entry,32,qt.hardlim);
+	param_len = 4;
+	params = nttrans_realloc(ppparams, param_len);
+	if(params == NULL) {
+		nt_status = NT_STATUS_NO_MEMORY;
+		goto error;
+	}
 
-			/* and now the SID */
-			sid_linearize((uint8_t *)(entry+40), sid_len, &sid);
+	SIVAL(params, 0, param_len);
 
-			break;
+	data_len = resp_len;
+	pdata = nttrans_realloc(ppdata, data_len);
 
-		default:
-			DEBUG(0, ("do_nt_transact_get_user_quota: %s: unknown "
-				  "level 0x%04hX\n",
-				  fsp_fnum_dbg(fsp), level));
-			reply_nterror(req, NT_STATUS_INVALID_LEVEL);
-			return;
-			break;
-	}
+	memcpy(pdata, resp_data, data_len);
 
+	TALLOC_FREE(tmp_ctx);
 	send_nt_replies(conn, req, nt_status, params, param_len,
 			pdata, data_len);
+	return;
+error:
+	TALLOC_FREE(tmp_ctx);
+	reply_nterror(req, nt_status);
 }
 
 /****************************************************************************
-- 
2.10.2


>From 5c6d8115c9272ad276b35dc093f24b6178c36e96 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 1 Mar 2017 14:19:14 +0000
Subject: [PATCH 8/9] s3/libsmb: Use ndr generated structures & routines for
 SMB[1|2] quota msgs.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/libsmb/cli_smb2_fnum.c | 75 ++++++++++++++++++++++++++----------------
 source3/libsmb/cliquota.c      | 74 +++++++++++++++--------------------------
 source3/wscript_build          |  1 +
 3 files changed, 74 insertions(+), 76 deletions(-)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 7977bea..f6322e44 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -41,6 +41,7 @@
 #include "lib/util_ea.h"
 #include "librpc/gen_ndr/ndr_ioctl.h"
 #include "ntioctl.h"
+#include "librpc/gen_ndr/ndr_quota.h"
 
 struct smb2_hnd {
 	uint64_t fid_persistent;
@@ -2395,12 +2396,15 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
 {
 	NTSTATUS status;
 	DATA_BLOB inbuf = data_blob_null;
+	DATA_BLOB info_blob = data_blob_null;
 	DATA_BLOB outbuf = data_blob_null;
 	struct smb2_hnd *ph = NULL;
 	TALLOC_CTX *frame = talloc_stackframe();
 	unsigned sid_len;
 	unsigned int offset;
-	uint8_t *buf;
+	struct smb2_query_quota_info query = {0};
+	struct file_get_quota_info info = {0};
+	enum ndr_err_code err;
 
 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 		/*
@@ -2422,27 +2426,40 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
 
 	sid_len = ndr_size_dom_sid(&pqt->sid, 0);
 
-	inbuf = data_blob_talloc_zero(frame, 24 + sid_len);
-	if (inbuf.data == NULL) {
-		status = NT_STATUS_NO_MEMORY;
+	query.return_single = 1;
+	if (sid_len < 0) {
+		status = NT_STATUS_INVALID_PARAMETER;
 		goto fail;
 	}
 
-	buf = inbuf.data;
+	info.next_entry_offset = 0;
+	info.sid_length = sid_len;
+	info.sid = pqt->sid;
 
-	SCVAL(buf, 0, 1);	   /* ReturnSingle */
-	SCVAL(buf, 1, 0);	   /* RestartScan */
-	SSVAL(buf, 2, 0);	   /* Reserved */
-	if (8 + sid_len < 8) {
-		status = NT_STATUS_INVALID_PARAMETER;
+	err = ndr_push_struct_blob(
+			&info_blob,
+			frame,
+			&info,
+			(ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto fail;
+	}
+
+	query.sid_list_length = info_blob.length;
+	query.sid_buf = info_blob.data;
+
+	err = ndr_push_struct_blob(
+			&inbuf,
+			frame,
+			&query,
+			(ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
+
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		status = NT_STATUS_INTERNAL_ERROR;
 		goto fail;
 	}
-	SIVAL(buf, 4, 8 + sid_len); /* SidListLength */
-	SIVAL(buf, 8, 0);	   /* StartSidLength */
-	SIVAL(buf, 12, 0);	  /* StartSidOffset */
-	SIVAL(buf, 16, 0);	  /* NextEntryOffset */
-	SIVAL(buf, 20, sid_len);    /* SidLength */
-	sid_linearize(buf + 24, sid_len, &pqt->sid);
 
 	status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
 				    cli->smb2.tcon, 4, /* in_info_type */
@@ -2487,7 +2504,8 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
 	DATA_BLOB outbuf = data_blob_null;
 	struct smb2_hnd *ph = NULL;
 	TALLOC_CTX *frame = talloc_stackframe();
-	uint8_t *buf;
+	struct smb2_query_quota_info info = {0};
+	enum ndr_err_code err;
 
 	if (smbXcli_conn_has_async_calls(cli->conn)) {
 		/*
@@ -2507,20 +2525,19 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
 		goto cleanup;
 	}
 
-	inbuf = data_blob_talloc_zero(frame, 16);
-	if (inbuf.data == NULL) {
-		status = NT_STATUS_NO_MEMORY;
-		goto cleanup;
-	}
 
-	buf = inbuf.data;
+	info.restart_scan = first ? 1 : 0;
+
+	err = ndr_push_struct_blob(
+			&inbuf,
+			frame,
+			&info,
+			(ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
 
-	SCVAL(buf, 0, 0);	     /* ReturnSingle */
-	SCVAL(buf, 1, first ? 1 : 0); /* RestartScan */
-	SSVAL(buf, 2, 0);	     /* Reserved */
-	SIVAL(buf, 4, 0);	     /* SidListLength */
-	SIVAL(buf, 8, 0);	     /* StartSidLength */
-	SIVAL(buf, 12, 0);	    /* StartSidOffset */
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		status = NT_STATUS_INTERNAL_ERROR;
+		goto cleanup;
+	}
 
 	status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
 				    cli->smb2.tcon, 4, /* in_info_type */
diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index e22ccdd..c0e163c 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -24,6 +24,7 @@
 #include "../libcli/security/security.h"
 #include "trans2.h"
 #include "../libcli/smb/smbXcli_base.h"
+#include "librpc/gen_ndr/ndr_quota.h"
 
 NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
 {
@@ -75,61 +76,40 @@ bool parse_user_quota_record(const uint8_t *rdata,
 			     unsigned int *offset,
 			     SMB_NTQUOTA_STRUCT *pqt)
 {
-	int sid_len;
-	SMB_NTQUOTA_STRUCT qt;
-
-	ZERO_STRUCT(qt);
-
-	if (!rdata||!offset||!pqt) {
-		smb_panic("parse_quota_record: called with NULL POINTER!");
-	}
-
-	if (rdata_count < 40) {
-		return False;
-	}
-
-	/* offset to next quota record.
-	 * 4 bytes IVAL(rdata,0)
-	 * unused here...
-	 */
-	*offset = IVAL(rdata,0);
-
-	/* sid len */
-	sid_len = IVAL(rdata,4);
-	if (40 + sid_len < 40) {
-		return false;
-	}
+	struct file_quota_information info = {0};
+	TALLOC_CTX *frame = talloc_stackframe();
+	DATA_BLOB blob;
+	enum ndr_err_code err;
+	bool result;
 
-	if (rdata_count < 40+sid_len) {
-		return False;		
-	}
+	blob.data = discard_const_p(uint8_t, rdata);
+	blob.length = rdata_count;
+	err = ndr_pull_struct_blob(
+			&blob,
+			frame,
+			&info,
+			(ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
 
-	if (*offset != 0 && *offset < 40 + sid_len) {
-		return false;
+	if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+		result = false;
+		goto out;
 	}
 
-	/* unknown 8 bytes in pdata 
-	 * maybe its the change time in NTTIME
-	 */
-
-	/* the used space 8 bytes (uint64_t)*/
-	qt.usedspace = BVAL(rdata,16);
-
-	/* the soft quotas 8 bytes (uint64_t)*/
-	qt.softlim = BVAL(rdata,24);
-
-	/* the hard quotas 8 bytes (uint64_t)*/
-	qt.hardlim = BVAL(rdata,32);
+	*offset = info.next_entry_offset;
 
-	if (!sid_parse(rdata+40,sid_len,&qt.sid)) {
-		return false;
-	}
+	ZERO_STRUCTP(pqt);
+	pqt->usedspace = info.quota_used;
 
-	qt.qtype = SMB_USER_QUOTA_TYPE;
+	pqt->softlim = info.quota_threshold;
 
-	*pqt = qt;
+	pqt->hardlim = info.quota_limit;
 
-	return True;
+	pqt->qtype = SMB_USER_QUOTA_TYPE;
+	pqt->sid = info.sid;
+	result = true;
+out:
+	TALLOC_FREE(frame);
+	return result;
 }
 
 NTSTATUS parse_user_quota_list(const uint8_t *curdata,
diff --git a/source3/wscript_build b/source3/wscript_build
index 3414761..54234ad 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -487,6 +487,7 @@ bld.SAMBA3_LIBRARY('libsmb',
                         LIBTSOCKET
                         KRBCLIENT
                         NDR_IOCTL
+                        NDR_QUOTA
                         cli_smb_common
                         util_cmdline
                         tevent
-- 
2.10.2


>From e54b739af7f8ff3f583f3b678ca22702d4e8c9c0 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 1 Mar 2017 16:51:06 +0000
Subject: [PATCH 9/9] s3/libsmb: Modify build_user_quota_buffer with ndr
 structs and push/pull funcs.

Signed-off-by: Noel Power <noel.power at suse.com>
---
 source3/libsmb/cliquota.c | 149 ++++++++++++++++++++--------------------------
 1 file changed, 63 insertions(+), 86 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index c0e163c..3159265 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -197,111 +197,80 @@ NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
 				 DATA_BLOB *outbuf,
 				 SMB_NTQUOTA_LIST **end_ptr)
 {
-	uint32_t qt_len = 0;
-	uint8_t *entry;
-	uint32_t entry_len;
-	int sid_len;
-	SMB_NTQUOTA_LIST *qtl;
-	DATA_BLOB qbuf = data_blob_null;
-	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
-
+	int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+	struct ndr_push *header_ndr = ndr_push_init_ctx(mem_ctx);
+	uint32_t start_offset = 0;
+	uint32_t num_pushed = 0;
+	uint32_t next_align = 0;
+	bool max_data_exceeded = false;
 	if (qt_list == NULL) {
-		status = NT_STATUS_OK;
-		*outbuf = data_blob_null;
-		if (end_ptr) {
-			*end_ptr = NULL;
+		return NT_STATUS_NO_MORE_ENTRIES;
+	}
+	for (;qt_list!=NULL; qt_list=qt_list->next, num_pushed++) {
+		struct file_quota_information info;
+		enum ndr_err_code err;
+		ZERO_STRUCT(info);
+		info.sid_length = ndr_size_dom_sid(&qt_list->quotas->sid, 0);
+
+		if (maxlen && ((header_ndr->offset - next_align)
+		   + sizeof(info.next_entry_offset)
+		   + sizeof(info.sid_length)
+		   + sizeof(info.change_time)
+		   + sizeof(info.quota_used)
+		   + sizeof(info.quota_threshold)
+		   + sizeof(info.quota_limit)
+		   + info.sid_length) > maxlen) {
+			max_data_exceeded = true;
+			break;
 		}
-		return NT_STATUS_OK;
-	}
+		start_offset = header_ndr->offset;
 
-	for (qtl = qt_list; qtl != NULL; qtl = qtl->next) {
+		info.sid = qt_list->quotas->sid;
+		info.quota_used = qt_list->quotas->usedspace;
+		info.quota_threshold = qt_list->quotas->softlim;
+		info.quota_limit = qt_list->quotas->hardlim;
 
-		sid_len = ndr_size_dom_sid(&qtl->quotas->sid, 0);
-		if (47 + sid_len < 47) {
-			status = NT_STATUS_INVALID_PARAMETER;
-			goto fail;
-		}
-		entry_len = 40 + sid_len;
-		entry_len = ((entry_len + 7) / 8) * 8;
+		err = ndr_push_file_quota_information(header_ndr,
+						      ndr_flags,
+						      &info);
 
-		if (qt_len + entry_len < qt_len) {
-			status = NT_STATUS_INVALID_PARAMETER;
-			goto fail;
+		if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+			DBG_DEBUG("Failed to pull the quota sid\n");
+			return NT_STATUS_INTERNAL_ERROR;
 		}
-		qt_len += entry_len;
-	}
 
-	if (maxlen > 0 && qt_len > maxlen) {
-		qt_len = maxlen;
-	}
+		/* pidl will align to 8 bytes due to 8 byte members*/
+		next_align = header_ndr->offset;
+		ndr_push_align(header_ndr, 8);
+		next_align = header_ndr->offset - next_align;
+		info.next_entry_offset = header_ndr->offset - start_offset;
 
-	qbuf = data_blob_talloc_zero(mem_ctx, qt_len);
-	if (qbuf.data == NULL) {
-		status = NT_STATUS_NO_MEMORY;
-		goto fail;
+		/* write nextentryoffset to the buffer too */
+		SIVAL(header_ndr->data, start_offset, info.next_entry_offset);
 	}
 
-	for (qt_len = 0, entry = qbuf.data; qt_list != NULL;
-	     qt_list = qt_list->next, qt_len += entry_len, entry += entry_len) {
-
-		sid_len = ndr_size_dom_sid(&qt_list->quotas->sid, 0);
-		entry_len = 40 + sid_len;
-		entry_len = ((entry_len + 7) / 8) * 8;
-
-		if (qt_len + entry_len > qbuf.length) {
-			/* check for not-enough room even for a single
-			 * entry
-			 */
-			if (qt_len == 0) {
-				status = NT_STATUS_BUFFER_TOO_SMALL;
-				goto fail;
-			}
-
-			break;
-		}
-
-		/* nextoffset entry 4 bytes */
-		SIVAL(entry, 0, entry_len);
 
-		/* then the len of the SID 4 bytes */
-		SIVAL(entry, 4, sid_len);
+	outbuf->length =  header_ndr->offset - next_align;
+	outbuf->data = header_ndr->data;
 
-		/* NTTIME of last record change */
-		SBIG_UINT(entry, 8, (uint64_t)0);
-
-		/* the used disk space 8 bytes uint64_t */
-		SBIG_UINT(entry, 16, qt_list->quotas->usedspace);
-
-		/* the soft quotas 8 bytes uint64_t */
-		SBIG_UINT(entry, 24, qt_list->quotas->softlim);
-
-		/* the hard quotas 8 bytes uint64_t */
-		SBIG_UINT(entry, 32, qt_list->quotas->hardlim);
-
-		/* and now the SID */
-		sid_linearize((uint8_t *)(entry + 40), sid_len,
-			      &qt_list->quotas->sid);
+	if (num_pushed) {
+		/* terminate the list */
+		SIVAL(header_ndr->data, start_offset, 0);
 	}
 
-	/* overwrite the offset of the last entry */
-	SIVAL(entry - entry_len, 0, 0);
-
-	/*potentially shrink the buffer if max was given
-	 * and we haven't quite reached the max
+	/*
+	 * if we failed to write a single file_quota_information
+	 * issue BUFFER_TOO_SMALL
 	 */
-	qbuf.length = qt_len;
-	*outbuf = qbuf;
-	qbuf = data_blob_null;
-	status = NT_STATUS_OK;
+	if (max_data_exceeded && num_pushed < 1) {
+		return NT_STATUS_BUFFER_TOO_SMALL;
+	}
 
 	if (end_ptr) {
 		*end_ptr = qt_list;
 	}
 
-fail:
-	data_blob_free(&qbuf);
-
-	return status;
+	return NT_STATUS_OK;
 }
 
 NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
@@ -422,7 +391,15 @@ cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
 
 	status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
 	if (!NT_STATUS_IS_OK(status)) {
-		goto cleanup;
+		/*
+		 * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
+		 * this status.
+		 */
+		if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+			status = NT_STATUS_OK;
+		} else {
+			goto cleanup;
+		}
 	}
 
 	SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
-- 
2.10.2



More information about the samba-technical mailing list