[PATCHES] SMB2 client quota support

Jeremy Allison jra at samba.org
Mon Sep 26 20:05:14 UTC 2016


On Thu, Sep 22, 2016 at 08:09:18PM +0300, Uri Simchoni wrote:
> Hi,
> 
> Strange things can happen in those jet-lagged nights.
> 
> The attached patch set fixes a few issues with current quota support
> code and then adds support for using smbcquotas with SMB2 and later
> protocols.
> 
> Unlike the server-side lack of support, I don't consider smb2 client
> quota to be a bugfix, but rather a feature, because a user can use tool
> A in SMB1 mode and tool B in SMB2 mode, whereas with the server, a mount
> dictates the protocol version of all operations, so if suddenly quota
> doesn't work that's a degradation.
> 
> I intend to follow up with fixes to the server as well, and some of the
> work here lays the ground work for that, but the patch has grown quite a
> bit so I decided to stop here and submit it.
> 
> I also upstreamed wireshark patches to dissect it all.
> 
> The first 5 patches are bugfixes and the rest is cleanup / adding the
> smb2 stuff. It can be squashed quite a bit since it's all around the
> same 3-4 files - I'm open to suggestions.
> 
> Review appreciated,

1). Hate this reformatting... :-)

[PATCH 07/19] s3-libsmb: make parse_user_quota_record() public

-static bool parse_user_quota_record(const uint8_t *rdata,
-                                   unsigned int rdata_count,
-                                   unsigned int *offset,
-                                   SMB_NTQUOTA_STRUCT *pqt)
+bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
+                            unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)

I much prefer the

function(arg1
	arg2,
	arg3.
	arg4)

style. But that's just a personal thing :-).

2). In [PATCH 08/19] s3-libsmb: support getting user's quota in SMB2

+       SIVAL(buf, 4, 8 + sid_len); /* SidListLength */

wrap check on 8 + sid_len please. I know it almost
certainly can't wrap, but I want to beat into everyone's
skulls when doing network marshalling in C *ALWAYS CHECK
FOR INTEGER WRAP !!!*.

I have a vain hope it'll prevent a security bug someday :-).

3). [PATCH 10/19] cliquota: some security hardening

+       if (*offset != 0 && *offset < 40 + sid_len) {
+               return false;
+       }

wrap check on 40 + sid_len please !

4). [PATCH 15/19] cliquota: factor out building of FILE_QUOTA_INFORMATION

+       for (qtl = qt_list; qtl != NULL; qtl = qtl->next, qt_len += entry_len) {
+
+               sid_len = ndr_size_dom_sid(&qtl->quotas->sid, 0);
+               entry_len = 40 + sid_len;
+               /* Fixme: do we have a standard "round-up" primitive? */
+               entry_len = ((entry_len + 7) / 8) * 8;
+       }

wrap checks here please.

+               sid_len = ndr_size_dom_sid(&qt_list->quotas->sid, 0);
+               entry_len = 40 + sid_len;
+               /* Fixme: do we have a standard "round-up" primitive? */
+               entry_len = ((entry_len + 7) / 8) * 8;

and here.

Otherwise looks good ! As a follow-up, in:

[PATCH 01/19] s3-cliquota: correctly handle no-more-entries

When listing quota records, a Windows server would
return STATUS_SUCCESS until no more entries are available,
where it would return STATUS_NO_MORE_ENTRIES.

Should we fix smbd to do the same as Windows here ?

Jeremy.

> From f638503164c99eef468e43688c2588121b5b6c11 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Fri, 16 Sep 2016 21:57:50 +0300
> Subject: [PATCH 01/19] s3-cliquota: correctly handle no-more-entries
> 
> When listing quota records, a Windows server would
> return STATUS_SUCCESS until no more entries are available,
> where it would return STATUS_NO_MORE_ENTRIES.
> 
> The fix keeps old behavior of empty answer also signifying
> end of record, to maintain compatibility with Samba servers.
> 
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=12270
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 13 +++++++++----
>  1 file changed, 9 insertions(+), 4 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 875c419..778fefc 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -242,13 +242,15 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  			   &rparam, 0, &rparam_count,
>  			   &rdata, 0, &rdata_count);
>  
> -	if (!NT_STATUS_IS_OK(status)) {
> +	if (!NT_STATUS_IS_OK(status) &&
> +	    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
>  		DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
>  			  nt_errstr(status)));
>  		goto cleanup;
>  	}
>  
> -	if (rdata_count == 0) {
> +	if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
> +	    rdata_count == 0) {
>  		*pqt_list = NULL;
>  		return NT_STATUS_OK;
>  	}
> @@ -304,13 +306,16 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  				   &rparam, 0, &rparam_count,
>  				   &rdata, 0, &rdata_count);
>  
> -		if (!NT_STATUS_IS_OK(status)) {
> +		if (!NT_STATUS_IS_OK(status) &&
> +		    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
>  			DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
>  				  nt_errstr(status)));
>  			goto cleanup;
>  		}
>  
> -		if (rdata_count == 0) {
> +		if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
> +		    rdata_count == 0) {
> +			status = NT_STATUS_OK;
>  			break;
>  		}
>  
> -- 
> 2.5.5
> 
> 
> From da7153e83f162b88c333e015cec5d62abcb3f0bb Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Fri, 16 Sep 2016 22:01:46 +0300
> Subject: [PATCH 02/19] smbcquotas: fix error message listing quotas
> 
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=12270
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/utils/smbcquotas.c | 6 +++---
>  1 file changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
> index e6f1dfb..cda5f92 100644
> --- a/source3/utils/smbcquotas.c
> +++ b/source3/utils/smbcquotas.c
> @@ -408,9 +408,9 @@ static int do_quota(struct cli_state *cli,
>  					status = cli_list_user_quota(
>  						cli, quota_fnum, &qtl);
>  					if (!NT_STATUS_IS_OK(status)) {
> -						d_printf("%s cli_set_user_quota %s\n",
> -							 nt_errstr(status),
> -							 username_str);
> +						d_printf(
> +						    "%s cli_list_user_quota\n",
> +						    nt_errstr(status));
>  						return -1;
>  					}
>  					dump_ntquota_list(&qtl,verbose,numeric,SidToString);
> -- 
> 2.5.5
> 
> 
> From 5cf9f097905db57079527cf4a6b64724c0a3dbe9 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Sun, 18 Sep 2016 11:09:54 +0300
> Subject: [PATCH 03/19] ntquotas: support "freeing" an empty quota list
> 
> This avoids dereferencing a null pointer if there's
> an attempt to free an empty list.
> 
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=12270
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 3 ++-
>  source3/smbd/ntquotas.c   | 3 +--
>  2 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 778fefc..a2c6edd 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -34,8 +34,9 @@ NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
>  
>  void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
>  {
> -	if (!qt_list)
> +	if (!qt_list || !*qt_list) {
>  		return;
> +	}
>  
>  	if ((*qt_list)->mem_ctx)
>  		talloc_destroy((*qt_list)->mem_ctx);
> diff --git a/source3/smbd/ntquotas.c b/source3/smbd/ntquotas.c
> index 9b2e39a..93f4a2a 100644
> --- a/source3/smbd/ntquotas.c
> +++ b/source3/smbd/ntquotas.c
> @@ -231,8 +231,7 @@ int vfs_get_user_ntquota_list(files_struct *fsp, SMB_NTQUOTA_LIST **qt_list)
>  
>  static int quota_handle_destructor(SMB_NTQUOTA_HANDLE *handle)
>  {
> -	if (handle->quota_list)
> -		free_ntquota_list(&handle->quota_list);
> +	free_ntquota_list(&handle->quota_list);
>  	return 0;
>  }
>  
> -- 
> 2.5.5
> 
> 
> From bc87d94ac9338e1808925c7883e59b3c3e5b9ecb Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Thu, 22 Sep 2016 16:06:12 +0300
> Subject: [PATCH 04/19] cliquota: fix param count when setting fs quota
> 
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=12270
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index a2c6edd..ce48257 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -451,7 +451,7 @@ NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  			   NULL, -1, /* name, fid */
>  			   0, 0,     /* function, flags */
>  			   setup, 1, 0, /* setup */
> -			   param, 8, 0, /* param */
> +			   param, 4, 0, /* param */
>  			   data, 48, 0, /* data */
>  			   NULL,	 /* recv_flags2 */
>  			   NULL, 0, NULL, /* rsetup */
> -- 
> 2.5.5
> 
> 
> From d2aff728c7f2c6af680224d0ea8dc03e4547e967 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Sun, 18 Sep 2016 11:05:23 +0300
> Subject: [PATCH 05/19] smbd: free talloc context if no quota records are
>  available
> 
> When generating a list of user quota records, free the memory
> context that controls this list if the list is empty. Otherwise,
> the context remains unreferenced and memory is leaked.
> 
> BUG: https://bugzilla.samba.org/show_bug.cgi?id=12289
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/smbd/ntquotas.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/source3/smbd/ntquotas.c b/source3/smbd/ntquotas.c
> index 93f4a2a..4acfa50 100644
> --- a/source3/smbd/ntquotas.c
> +++ b/source3/smbd/ntquotas.c
> @@ -226,6 +226,9 @@ int vfs_get_user_ntquota_list(files_struct *fsp, SMB_NTQUOTA_LIST **qt_list)
>  	}
>  	endpwent();
>  
> +	if (*qt_list == NULL) {
> +		TALLOC_FREE(mem_ctx);
> +	}
>  	return 0;
>  }
>  
> -- 
> 2.5.5
> 
> 
> From 052944e44c02646c7deffea0d80d504c179ace5d Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Tue, 20 Sep 2016 14:32:06 +0300
> Subject: [PATCH 06/19] s3-libsmb: Support getting fs attributes via SMB2
> 
> Add a wrapper function arounf GET_INFO to obtain
> file system attributes, and plumb it in.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 77 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  1 +
>  source3/libsmb/clifsinfo.c     |  4 +++
>  3 files changed, 82 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index ac72090..a998d6f 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -1756,6 +1756,83 @@ NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path,
>  }
>  
>  /***************************************************************
> + Wrapper that allows SMB2 to query file system attributes.
> + Synchronous only.
> +***************************************************************/
> +
> +NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
> +{
> +	NTSTATUS status;
> +	uint16_t fnum = 0xffff;
> +	DATA_BLOB outbuf = data_blob_null;
> +	struct smb2_hnd *ph = NULL;
> +	TALLOC_CTX *frame = talloc_stackframe();
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto fail;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto fail;
> +	}
> +
> +	/* First open the top level directory. */
> +	status =
> +	    cli_smb2_create_fnum(cli, "", 0,		   /* create_flags */
> +				 FILE_READ_ATTRIBUTES,     /* desired_access */
> +				 FILE_ATTRIBUTE_DIRECTORY, /* file attributes */
> +				 FILE_SHARE_READ | FILE_SHARE_WRITE |
> +				     FILE_SHARE_DELETE, /* share_access */
> +				 FILE_OPEN,		/* create_disposition */
> +				 FILE_DIRECTORY_FILE,   /* create_options */
> +				 &fnum,
> +				 NULL);
> +
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto fail;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto fail;
> +	}
> +
> +	status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
> +				    cli->smb2.tcon, 2, /* in_info_type */
> +				    5,		       /* in_file_info_class */
> +				    0xFFFF, /* in_max_output_length */
> +				    NULL,   /* in_input_buffer */
> +				    0,      /* in_additional_info */
> +				    0,      /* in_flags */
> +				    ph->fid_persistent, ph->fid_volatile, frame,
> +				    &outbuf);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto fail;
> +	}
> +
> +	if (outbuf.length < 12) {
> +		status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +		goto fail;
> +	}
> +
> +	*fs_attr = IVAL(outbuf.data, 0);
> +
> +fail:
> +
> +	if (fnum != 0xffff) {
> +		cli_smb2_close_fnum(cli, fnum);
> +	}
> +
> +	TALLOC_FREE(frame);
> +	return status;
> +}
> +
> +/***************************************************************
>   Wrapper that allows SMB2 to query a security descriptor.
>   Synchronous only.
>  ***************************************************************/
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index 0436c68..2df54a3 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -121,6 +121,7 @@ NTSTATUS cli_smb2_dskattr(struct cli_state *cli,
>  			uint64_t *bsize,
>  			uint64_t *total,
>  			uint64_t *avail);
> +NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr);
>  NTSTATUS cli_smb2_query_security_descriptor(struct cli_state *cli,
>  			uint16_t fnum,
>  			uint32_t sec_info,
> diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c
> index ddc9efd..31a2c02 100644
> --- a/source3/libsmb/clifsinfo.c
> +++ b/source3/libsmb/clifsinfo.c
> @@ -337,6 +337,10 @@ NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr)
>  	struct tevent_req *req;
>  	NTSTATUS status = NT_STATUS_NO_MEMORY;
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_get_fs_attr_info(cli, fs_attr);
> +	}
> +
>  	if (smbXcli_conn_has_async_calls(cli->conn)) {
>  		return NT_STATUS_INVALID_PARAMETER;
>  	}
> -- 
> 2.5.5
> 
> 
> From 79bc9e193f306f46ad5d462c078c034aa2c74d92 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Mon, 12 Sep 2016 22:33:12 +0300
> Subject: [PATCH 07/19] s3-libsmb: make parse_user_quota_record() public
> 
> For reuse by SMB2 client code.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 6 ++----
>  source3/libsmb/proto.h    | 2 ++
>  2 files changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index ce48257..f3d93ae 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -46,10 +46,8 @@ void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
>  	return;	
>  }
>  
> -static bool parse_user_quota_record(const uint8_t *rdata,
> -				    unsigned int rdata_count,
> -				    unsigned int *offset,
> -				    SMB_NTQUOTA_STRUCT *pqt)
> +bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
> +			     unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
>  {
>  	int sid_len;
>  	SMB_NTQUOTA_STRUCT qt;
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index c0e1b74..879d713 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -762,6 +762,8 @@ int cli_printjob_del(struct cli_state *cli, int job);
>  
>  NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum);
>  void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list);
> +bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
> +			     unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -- 
> 2.5.5
> 
> 
> From 1d6a63751065cb98f4fa92378dfc42566f509940 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Mon, 12 Sep 2016 22:38:15 +0300
> Subject: [PATCH 08/19] s3-libsmb: support getting user's quota in SMB2
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 81 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  2 ++
>  source3/libsmb/cliquota.c      |  5 +++
>  3 files changed, 88 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index a998d6f..7122731 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -37,6 +37,7 @@
>  #include "libsmb/proto.h"
>  #include "lib/util/tevent_ntstatus.h"
>  #include "../libcli/security/security.h"
> +#include "../librpc/gen_ndr/ndr_security.h"
>  #include "lib/util_ea.h"
>  #include "librpc/gen_ndr/ndr_ioctl.h"
>  #include "ntioctl.h"
> @@ -2347,6 +2348,86 @@ NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli,
>  	return status;
>  }
>  
> +/***************************************************************
> + Wrapper that allows SMB2 to get user quota.
> + Synchronous only.
> +***************************************************************/
> +
> +NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, int quota_fnum,
> +				 SMB_NTQUOTA_STRUCT *pqt)
> +{
> +	NTSTATUS status;
> +	DATA_BLOB inbuf = 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;
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto fail;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto fail;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto fail;
> +	}
> +
> +	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;
> +		goto fail;
> +	}
> +
> +	buf = inbuf.data;
> +
> +	SCVAL(buf, 0, 1);	   /* ReturnSingle */
> +	SCVAL(buf, 1, 0);	   /* RestartScan */
> +	SSVAL(buf, 2, 0);	   /* Reserved */
> +	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 */
> +				    0,		       /* in_file_info_class */
> +				    0xFFFF, /* in_max_output_length */
> +				    &inbuf, /* in_input_buffer */
> +				    0,      /* in_additional_info */
> +				    0,      /* in_flags */
> +				    ph->fid_persistent, ph->fid_volatile, frame,
> +				    &outbuf);
> +
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto fail;
> +	}
> +
> +	if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset,
> +				     pqt)) {
> +		status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +		DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n"));
> +	}
> +
> +fail:
> +	TALLOC_FREE(frame);
> +	return status;
> +}
> +
>  struct cli_smb2_read_state {
>  	struct tevent_context *ev;
>  	struct cli_state *cli;
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index 2df54a3..c60316f 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -149,6 +149,8 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
>  			const char *ea_name,
>  			const char *ea_val,
>  			size_t ea_len);
> +NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, int quota_fnum,
> +				 SMB_NTQUOTA_STRUCT *pqt);
>  struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
>  				struct tevent_context *ev,
>  				struct cli_state *cli,
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index f3d93ae..c9a7f57 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -23,6 +23,7 @@
>  #include "fake_file.h"
>  #include "../libcli/security/security.h"
>  #include "trans2.h"
> +#include "../libcli/smb/smbXcli_base.h"
>  
>  NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
>  {
> @@ -116,6 +117,10 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  		smb_panic("cli_get_user_quota() called with NULL Pointer!");
>  	}
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
> +	}
> +
>  	SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
>  
>  	SSVAL(params, 0,quota_fnum);
> -- 
> 2.5.5
> 
> 
> From a71cee9ceada5f5ddebbe8eed1f45c31835e046e Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Sun, 18 Sep 2016 11:13:16 +0300
> Subject: [PATCH 09/19] cliquota: refactor and cleanup listing of user quotas
> 
> Split cli_list_user_quota into an outer loop function and
> an inner loop function.
> 
> This simplifies the code somewhat, paves the way for SMB2
> support, and fixes a couple of memory leaks in error
> conditions. No functional changes.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 135 +++++++++++++++++-----------------------------
>  1 file changed, 50 insertions(+), 85 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index c9a7f57..7771d3c 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -208,8 +208,10 @@ NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
>  	return status;
>  }
>  
> -NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
> -			     SMB_NTQUOTA_LIST **pqt_list)
> +static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
> +					 TALLOC_CTX *mem_ctx, int quota_fnum,
> +					 SMB_NTQUOTA_LIST **pqt_list,
> +					 bool first)
>  {
>  	uint16_t setup[1];
>  	uint8_t params[16];
> @@ -218,19 +220,16 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  	unsigned int offset;
>  	const uint8_t *curdata = NULL;
>  	unsigned int curdata_count = 0;
> -	TALLOC_CTX *mem_ctx = NULL;
>  	SMB_NTQUOTA_STRUCT qt;
>  	SMB_NTQUOTA_LIST *tmp_list_ent;
>  	NTSTATUS status;
> -
> -	if (!cli||!pqt_list) {
> -		smb_panic("cli_list_user_quota() called with NULL Pointer!");
> -	}
> +	uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
> +			    : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
>  
>  	SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
>  
>  	SSVAL(params, 0,quota_fnum);
> -	SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
> +	SSVAL(params, 2, op);
>  	SIVAL(params, 4,0x00000000);
>  	SIVAL(params, 8,0x00000000);
>  	SIVAL(params,12,0x00000000);
> @@ -248,20 +247,14 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  
>  	if (!NT_STATUS_IS_OK(status) &&
>  	    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
> -		DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
> -			  nt_errstr(status)));
>  		goto cleanup;
>  	}
>  
> -	if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
> -	    rdata_count == 0) {
> -		*pqt_list = NULL;
> -		return NT_STATUS_OK;
> -	}
> -
> -	if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
> -		DEBUG(0,("talloc_init() failed\n"));
> -		return NT_STATUS_NO_MEMORY;
> +	/* compat. with smbd + safeguard against
> +	 * endless loop
> +	 */
> +	if (rdata_count == 0) {
> +		status = NT_STATUS_NO_MORE_ENTRIES;
>  	}
>  
>  	offset = 1;
> @@ -272,19 +265,18 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  		if (!parse_user_quota_record((const uint8_t *)curdata, curdata_count,
>  					     &offset, &qt)) {
>  			DEBUG(1,("Failed to parse the quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
>  			goto cleanup;
>  		}
>  
>  		if ((tmp_list_ent=talloc_zero(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
> -			DEBUG(0,("TALLOC_ZERO() failed\n"));
> -			talloc_destroy(mem_ctx);
> -			return NT_STATUS_NO_MEMORY;
> +			status = NT_STATUS_NO_MEMORY;
> +			goto cleanup;
>  		}
>  
>  		if ((tmp_list_ent->quotas=talloc_zero(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
> -			DEBUG(0,("TALLOC_ZERO() failed\n"));
> -			talloc_destroy(mem_ctx);
> -			return NT_STATUS_NO_MEMORY;
> +			status = NT_STATUS_NO_MEMORY;
> +			goto cleanup;
>  		}
>  
>  		memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
> @@ -293,70 +285,43 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  		DLIST_ADD((*pqt_list),tmp_list_ent);
>  	}
>  
> -	SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE);	
> -	while(1) {
> -
> -		TALLOC_FREE(rparam);
> -		TALLOC_FREE(rdata);
> -
> -		status = cli_trans(talloc_tos(), cli, SMBnttrans,
> -				   NULL, -1, /* name, fid */
> -				   NT_TRANSACT_GET_USER_QUOTA, 0,
> -				   setup, 1, 0, /* setup */
> -				   params, 16, 4, /* params */
> -				   NULL, 0, 2048, /* data */
> -				   NULL,		/* recv_flags2 */
> -				   NULL, 0, NULL,	/* rsetup */
> -				   &rparam, 0, &rparam_count,
> -				   &rdata, 0, &rdata_count);
> -
> -		if (!NT_STATUS_IS_OK(status) &&
> -		    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
> -			DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
> -				  nt_errstr(status)));
> -			goto cleanup;
> -		}
> +cleanup:
> +	TALLOC_FREE(rparam);
> +	TALLOC_FREE(rdata);
>  
> -		if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
> -		    rdata_count == 0) {
> -			status = NT_STATUS_OK;
> -			break;
> -		}
> +	return status;
> +}
>  
> -		offset = 1;
> -		for (curdata=rdata,curdata_count=rdata_count;
> -			((curdata)&&(curdata_count>=8)&&(offset>0));
> -			curdata +=offset,curdata_count -= offset) {
> -			ZERO_STRUCT(qt);
> -			if (!parse_user_quota_record((const uint8_t *)curdata,
> -						     curdata_count, &offset,
> -						     &qt)) {
> -				DEBUG(1,("Failed to parse the quota record\n"));
> -				goto cleanup;
> -			}
> -
> -			if ((tmp_list_ent=talloc_zero(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
> -				DEBUG(0,("TALLOC_ZERO() failed\n"));
> -				talloc_destroy(mem_ctx);
> -				goto cleanup;
> -			}
> -
> -			if ((tmp_list_ent->quotas=talloc_zero(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
> -				DEBUG(0,("TALLOC_ZERO() failed\n"));
> -				talloc_destroy(mem_ctx);
> -				goto cleanup;
> -			}
> -
> -			memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
> -			tmp_list_ent->mem_ctx = mem_ctx;		
> -
> -			DLIST_ADD((*pqt_list),tmp_list_ent);
> -		}
> +NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
> +			     SMB_NTQUOTA_LIST **pqt_list)
> +{
> +	NTSTATUS status;
> +	TALLOC_CTX *mem_ctx = NULL;
> +	bool first = true;
> +
> +	if (!cli || !pqt_list) {
> +		smb_panic("cli_list_user_quota() called with NULL Pointer!");
>  	}
>  
> - cleanup:
> -	TALLOC_FREE(rparam);
> -	TALLOC_FREE(rdata);
> +	*pqt_list = NULL;
> +
> +	if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
> +		return NT_STATUS_NO_MEMORY;
> +	}
> +
> +	do {
> +		status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
> +						  pqt_list, first);
> +		first = false;
> +	} while (NT_STATUS_IS_OK(status));
> +
> +	if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
> +		status = NT_STATUS_OK;
> +	}
> +
> +	if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
> +		TALLOC_FREE(mem_ctx);
> +	}
>  
>  	return status;
>  }
> -- 
> 2.5.5
> 
> 
> From 948c6d1c4c6f292e1be63b95296d7b6bd62840d9 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Mon, 19 Sep 2016 18:24:58 +0300
> Subject: [PATCH 10/19] cliquota: some security hardening
> 
> Add some checks for validity of the offset in
> the return buffer.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 30 ++++++++++++++++++++++++++----
>  1 file changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 7771d3c..74ca033 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -76,6 +76,10 @@ bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  		return False;		
>  	}
>  
> +	if (*offset != 0 && *offset < 40 + sid_len) {
> +		return false;
> +	}
> +
>  	/* unknown 8 bytes in pdata 
>  	 * maybe its the change time in NTTIME
>  	 */
> @@ -257,10 +261,9 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
>  		status = NT_STATUS_NO_MORE_ENTRIES;
>  	}
>  
> -	offset = 1;
> -	for (curdata=rdata,curdata_count=rdata_count;
> -		((curdata)&&(curdata_count>=8)&&(offset>0));
> -		curdata +=offset,curdata_count -= offset) {
> +	curdata = rdata;
> +	curdata_count = rdata_count;
> +	while (true) {
>  		ZERO_STRUCT(qt);
>  		if (!parse_user_quota_record((const uint8_t *)curdata, curdata_count,
>  					     &offset, &qt)) {
> @@ -283,6 +286,25 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
>  		tmp_list_ent->mem_ctx = mem_ctx;		
>  
>  		DLIST_ADD((*pqt_list),tmp_list_ent);
> +
> +		if (offset > curdata_count) {
> +			DEBUG(1, ("out of bounds offset in quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +			goto cleanup;
> +		}
> +
> +		if (curdata + offset < curdata) {
> +			DEBUG(1, ("Pointer overflow in quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +			goto cleanup;
> +		}
> +
> +		curdata += offset;
> +		curdata_count -= offset;
> +
> +		if (offset == 0) {
> +			break;
> +		}
>  	}
>  
>  cleanup:
> -- 
> 2.5.5
> 
> 
> From ced7a984496034bce9345e51bc9d4b087f34e885 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Mon, 19 Sep 2016 21:14:01 +0300
> Subject: [PATCH 11/19] cliquota: factor out parsing of a quota record buffer
> 
> In preparation for SMB2 support, take parsing of the return
> buffer into a separate function.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 117 ++++++++++++++++++++++++----------------------
>  source3/libsmb/proto.h    |   3 ++
>  2 files changed, 65 insertions(+), 55 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 74ca033..0123810 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -104,6 +104,63 @@ bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  	return True;
>  }
>  
> +NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_count,
> +			       TALLOC_CTX *mem_ctx, SMB_NTQUOTA_LIST **pqt_list)
> +{
> +	NTSTATUS status = NT_STATUS_OK;
> +	unsigned offset;
> +	SMB_NTQUOTA_STRUCT qt;
> +	SMB_NTQUOTA_LIST *tmp_list_ent;
> +
> +	while (true) {
> +		ZERO_STRUCT(qt);
> +		if (!parse_user_quota_record(curdata, curdata_count, &offset,
> +					     &qt)) {
> +			DEBUG(1, ("Failed to parse the quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +			break;
> +		}
> +
> +		if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) ==
> +		    NULL) {
> +			status = NT_STATUS_NO_MEMORY;
> +			break;
> +		}
> +
> +		if ((tmp_list_ent->quotas =
> +			 talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) == NULL) {
> +			status = NT_STATUS_NO_MEMORY;
> +			break;
> +		}
> +
> +		memcpy(tmp_list_ent->quotas, &qt, sizeof(qt));
> +		tmp_list_ent->mem_ctx = mem_ctx;
> +
> +		DLIST_ADD((*pqt_list), tmp_list_ent);
> +
> +		if (offset > curdata_count) {
> +			DEBUG(1, ("out of bounds offset in quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +			break;
> +		}
> +
> +		if (curdata + offset < curdata) {
> +			DEBUG(1, ("Pointer overflow in quota record\n"));
> +			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> +			break;
> +		}
> +
> +		curdata += offset;
> +		curdata_count -= offset;
> +
> +		if (offset == 0) {
> +			break;
> +		}
> +	}
> +
> +	return status;
> +}
> +
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt)
>  {
> @@ -221,11 +278,6 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
>  	uint8_t params[16];
>  	uint8_t *rparam=NULL, *rdata=NULL;
>  	uint32_t rparam_count=0, rdata_count=0;
> -	unsigned int offset;
> -	const uint8_t *curdata = NULL;
> -	unsigned int curdata_count = 0;
> -	SMB_NTQUOTA_STRUCT qt;
> -	SMB_NTQUOTA_LIST *tmp_list_ent;
>  	NTSTATUS status;
>  	uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
>  			    : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
> @@ -249,64 +301,19 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
>  			   &rparam, 0, &rparam_count,
>  			   &rdata, 0, &rdata_count);
>  
> -	if (!NT_STATUS_IS_OK(status) &&
> -	    !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
> -		goto cleanup;
> -	}
> -
>  	/* compat. with smbd + safeguard against
>  	 * endless loop
>  	 */
> -	if (rdata_count == 0) {
> +	if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
>  		status = NT_STATUS_NO_MORE_ENTRIES;
>  	}
>  
> -	curdata = rdata;
> -	curdata_count = rdata_count;
> -	while (true) {
> -		ZERO_STRUCT(qt);
> -		if (!parse_user_quota_record((const uint8_t *)curdata, curdata_count,
> -					     &offset, &qt)) {
> -			DEBUG(1,("Failed to parse the quota record\n"));
> -			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> -			goto cleanup;
> -		}
> -
> -		if ((tmp_list_ent=talloc_zero(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
> -			status = NT_STATUS_NO_MEMORY;
> -			goto cleanup;
> -		}
> -
> -		if ((tmp_list_ent->quotas=talloc_zero(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
> -			status = NT_STATUS_NO_MEMORY;
> -			goto cleanup;
> -		}
> -
> -		memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
> -		tmp_list_ent->mem_ctx = mem_ctx;		
> -
> -		DLIST_ADD((*pqt_list),tmp_list_ent);
> -
> -		if (offset > curdata_count) {
> -			DEBUG(1, ("out of bounds offset in quota record\n"));
> -			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> -			goto cleanup;
> -		}
> -
> -		if (curdata + offset < curdata) {
> -			DEBUG(1, ("Pointer overflow in quota record\n"));
> -			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> -			goto cleanup;
> -		}
> -
> -		curdata += offset;
> -		curdata_count -= offset;
> -
> -		if (offset == 0) {
> -			break;
> -		}
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
>  	}
>  
> +	status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
> +
>  cleanup:
>  	TALLOC_FREE(rparam);
>  	TALLOC_FREE(rdata);
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index 879d713..28afbc1 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -764,6 +764,9 @@ NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum);
>  void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list);
>  bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  			     unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt);
> +NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_size,
> +			       TALLOC_CTX *mem_ctx,
> +			       SMB_NTQUOTA_LIST **pqt_list);
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -- 
> 2.5.5
> 
> 
> From fa2d8ad75de7b12afc958fa3d638f32a8dbb7cf7 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Mon, 19 Sep 2016 22:17:10 +0300
> Subject: [PATCH 12/19] cliquota: implement quota listing in SMB2
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 71 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  3 ++
>  source3/libsmb/cliquota.c      |  5 +++
>  3 files changed, 79 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index 7122731..c371439 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -2428,6 +2428,77 @@ fail:
>  	return status;
>  }
>  
> +/***************************************************************
> + Wrapper that allows SMB2 to list user quota.
> + Synchronous only.
> +***************************************************************/
> +
> +NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
> +				       TALLOC_CTX *mem_ctx, int quota_fnum,
> +				       SMB_NTQUOTA_LIST **pqt_list, bool first)
> +{
> +	NTSTATUS status;
> +	DATA_BLOB inbuf = data_blob_null;
> +	DATA_BLOB outbuf = data_blob_null;
> +	struct smb2_hnd *ph = NULL;
> +	TALLOC_CTX *frame = talloc_stackframe();
> +	uint8_t *buf;
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	inbuf = data_blob_talloc_zero(frame, 16);
> +	if (inbuf.data == NULL) {
> +		status = NT_STATUS_NO_MEMORY;
> +		goto cleanup;
> +	}
> +
> +	buf = inbuf.data;
> +
> +	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 */
> +
> +	status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
> +				    cli->smb2.tcon, 4, /* in_info_type */
> +				    0,		       /* in_file_info_class */
> +				    0xFFFF, /* in_max_output_length */
> +				    &inbuf, /* in_input_buffer */
> +				    0,      /* in_additional_info */
> +				    0,      /* in_flags */
> +				    ph->fid_persistent, ph->fid_volatile, frame,
> +				    &outbuf);
> +
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx,
> +				       pqt_list);
> +
> +cleanup:
> +	TALLOC_FREE(frame);
> +	return status;
> +}
> +
>  struct cli_smb2_read_state {
>  	struct tevent_context *ev;
>  	struct cli_state *cli;
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index c60316f..b8fc790 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -151,6 +151,9 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
>  			size_t ea_len);
>  NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, int quota_fnum,
>  				 SMB_NTQUOTA_STRUCT *pqt);
> +NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
> +				       TALLOC_CTX *mem_ctx, int quota_fnum,
> +				       SMB_NTQUOTA_LIST **pqt_list, bool first);
>  struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
>  				struct tevent_context *ev,
>  				struct cli_state *cli,
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 0123810..f3db45b 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -282,6 +282,11 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
>  	uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
>  			    : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
> +						     pqt_list, first);
> +	}
> +
>  	SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
>  
>  	SSVAL(params, 0,quota_fnum);
> -- 
> 2.5.5
> 
> 
> From be9fd4151d7e01cc84408060dc2a93073eb50b6d Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Tue, 20 Sep 2016 06:45:03 +0300
> Subject: [PATCH 13/19] cliquota: factor out fs quota parsing
> 
> This code will be reused by SMB2 code.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 50 +++++++++++++++++++++++++++++++----------------
>  source3/libsmb/proto.h    |  2 ++
>  2 files changed, 35 insertions(+), 17 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index f3db45b..ada5816 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -161,6 +161,38 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_count,
>  	return status;
>  }
>  
> +NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, unsigned int rdata_count,
> +			       SMB_NTQUOTA_STRUCT *pqt)
> +{
> +	SMB_NTQUOTA_STRUCT qt;
> +
> +	ZERO_STRUCT(qt);
> +
> +	if (rdata_count < 48) {
> +		/* minimum length is not enforced by SMB2 client.
> +		 */
> +		DEBUG(1, ("small returned fs quota buffer\n"));
> +		return NT_STATUS_INVALID_NETWORK_RESPONSE;
> +	}
> +
> +	/* unknown_1 24 NULL bytes in pdata*/
> +
> +	/* the soft quotas 8 bytes (uint64_t)*/
> +	qt.softlim = BVAL(rdata, 24);
> +
> +	/* the hard quotas 8 bytes (uint64_t)*/
> +	qt.hardlim = BVAL(rdata, 32);
> +
> +	/* quota_flags 2 bytes **/
> +	qt.qflags = SVAL(rdata, 40);
> +
> +	qt.qtype = SMB_USER_FS_QUOTA_TYPE;
> +
> +	*pqt = qt;
> +
> +	return NT_STATUS_OK;
> +}
> +
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt)
>  {
> @@ -367,11 +399,8 @@ NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  	uint8_t param[2];
>  	uint8_t *rdata=NULL;
>  	uint32_t rdata_count=0;
> -	SMB_NTQUOTA_STRUCT qt;
>  	NTSTATUS status;
>  
> -	ZERO_STRUCT(qt);
> -
>  	if (!cli||!pqt) {
>  		smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
>  	}
> @@ -397,20 +426,7 @@ NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  		return status;
>  	}
>  
> -	/* unknown_1 24 NULL bytes in pdata*/
> -
> -	/* the soft quotas 8 bytes (uint64_t)*/
> -	qt.softlim = BVAL(rdata,24);
> -
> -	/* the hard quotas 8 bytes (uint64_t)*/
> -	qt.hardlim = BVAL(rdata,32);
> -
> -	/* quota_flags 2 bytes **/
> -	qt.qflags = SVAL(rdata,40);
> -
> -	qt.qtype = SMB_USER_FS_QUOTA_TYPE;
> -
> -	*pqt = qt;
> +	status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
>  
>  	TALLOC_FREE(rdata);
>  	return status;
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index 28afbc1..075df01 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -767,6 +767,8 @@ bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_size,
>  			       TALLOC_CTX *mem_ctx,
>  			       SMB_NTQUOTA_LIST **pqt_list);
> +NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, unsigned int rdata_count,
> +			       SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -- 
> 2.5.5
> 
> 
> From ca42788b3c88dfbd21f888d533355fe06ff6804d Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Tue, 20 Sep 2016 06:46:28 +0300
> Subject: [PATCH 14/19] cliquota: support getting fs quota by SMB2
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 52 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  2 ++
>  source3/libsmb/cliquota.c      |  4 ++++
>  3 files changed, 58 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index c371439..e86d228 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -2499,6 +2499,58 @@ cleanup:
>  	return status;
>  }
>  
> +/***************************************************************
> + Wrapper that allows SMB2 to get file system quota.
> + Synchronous only.
> +***************************************************************/
> +
> +NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
> +				    SMB_NTQUOTA_STRUCT *pqt)
> +{
> +	NTSTATUS status;
> +	DATA_BLOB outbuf = data_blob_null;
> +	struct smb2_hnd *ph = NULL;
> +	TALLOC_CTX *frame = talloc_stackframe();
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = smb2cli_query_info(
> +	    cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
> +	    2,				     /* in_info_type */
> +	    SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
> +	    0xFFFF,			     /* in_max_output_length */
> +	    NULL,			     /* in_input_buffer */
> +	    0,				     /* in_additional_info */
> +	    0,				     /* in_flags */
> +	    ph->fid_persistent, ph->fid_volatile, frame, &outbuf);
> +
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt);
> +
> +cleanup:
> +	TALLOC_FREE(frame);
> +	return status;
> +}
> +
>  struct cli_smb2_read_state {
>  	struct tevent_context *ev;
>  	struct cli_state *cli;
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index b8fc790..0dfa543 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -154,6 +154,8 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, int quota_fnum,
>  NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
>  				       TALLOC_CTX *mem_ctx, int quota_fnum,
>  				       SMB_NTQUOTA_LIST **pqt_list, bool first);
> +NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
> +				    SMB_NTQUOTA_STRUCT *pqt);
>  struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
>  				struct tevent_context *ev,
>  				struct cli_state *cli,
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index ada5816..382bcbe 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -405,6 +405,10 @@ NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  		smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
>  	}
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
> +	}
> +
>  	SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
>  
>  	SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
> -- 
> 2.5.5
> 
> 
> From 99953c722d9089112af9a266e22e6b3615c9ea9d Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Wed, 21 Sep 2016 18:37:40 +0300
> Subject: [PATCH 15/19] cliquota: factor out building of FILE_QUOTA_INFORMATION
> 
> Add a function to build a FILE_QUOTA_INFORMATION buffer
> out of a quota list, and a function that adds a record
> to a quota list.
> 
> Some parameters of the new functions are unused by
> client code, but will be used by server code.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c  | 177 ++++++++++++++++++++++++++++++++++++---------
>  source3/libsmb/proto.h     |   7 +-
>  source3/utils/smbcquotas.c |  16 +++-
>  3 files changed, 163 insertions(+), 37 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 382bcbe..37c80ca 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -47,6 +47,28 @@ void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
>  	return;	
>  }
>  
> +bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx, SMB_NTQUOTA_STRUCT *pqt,
> +				SMB_NTQUOTA_LIST **pqt_list)
> +{
> +	SMB_NTQUOTA_LIST *tmp_list_ent;
> +
> +	if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) == NULL) {
> +		return false;
> +	}
> +
> +	if ((tmp_list_ent->quotas = talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) ==
> +	    NULL) {
> +		return false;
> +	}
> +
> +	*tmp_list_ent->quotas = *pqt;
> +	tmp_list_ent->mem_ctx = mem_ctx;
> +
> +	DLIST_ADD((*pqt_list), tmp_list_ent);
> +
> +	return true;
> +}
> +
>  bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  			     unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
>  {
> @@ -110,7 +132,6 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_count,
>  	NTSTATUS status = NT_STATUS_OK;
>  	unsigned offset;
>  	SMB_NTQUOTA_STRUCT qt;
> -	SMB_NTQUOTA_LIST *tmp_list_ent;
>  
>  	while (true) {
>  		ZERO_STRUCT(qt);
> @@ -121,23 +142,6 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_count,
>  			break;
>  		}
>  
> -		if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) ==
> -		    NULL) {
> -			status = NT_STATUS_NO_MEMORY;
> -			break;
> -		}
> -
> -		if ((tmp_list_ent->quotas =
> -			 talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) == NULL) {
> -			status = NT_STATUS_NO_MEMORY;
> -			break;
> -		}
> -
> -		memcpy(tmp_list_ent->quotas, &qt, sizeof(qt));
> -		tmp_list_ent->mem_ctx = mem_ctx;
> -
> -		DLIST_ADD((*pqt_list), tmp_list_ent);
> -
>  		if (offset > curdata_count) {
>  			DEBUG(1, ("out of bounds offset in quota record\n"));
>  			status = NT_STATUS_INVALID_NETWORK_RESPONSE;
> @@ -150,6 +154,11 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_count,
>  			break;
>  		}
>  
> +		if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) {
> +			status = NT_STATUS_NO_MEMORY;
> +			break;
> +		}
> +
>  		curdata += offset;
>  		curdata_count -= offset;
>  
> @@ -193,6 +202,109 @@ NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, unsigned int rdata_count,
>  	return NT_STATUS_OK;
>  }
>  
> +NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, uint32_t maxlen,
> +				 TALLOC_CTX *mem_ctx, 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;
> +
> +	if (qt_list == NULL) {
> +		status = NT_STATUS_OK;
> +		*outbuf = data_blob_null;
> +		if (end_ptr) {
> +			*end_ptr = NULL;
> +		}
> +		return NT_STATUS_OK;
> +	}
> +
> +	for (qtl = qt_list; qtl != NULL; qtl = qtl->next, qt_len += entry_len) {
> +
> +		sid_len = ndr_size_dom_sid(&qtl->quotas->sid, 0);
> +		entry_len = 40 + sid_len;
> +		/* Fixme: do we have a standard "round-up" primitive? */
> +		entry_len = ((entry_len + 7) / 8) * 8;
> +	}
> +
> +	if (maxlen > 0 && qt_len > maxlen) {
> +		qt_len = maxlen;
> +	}
> +
> +	qbuf = data_blob_talloc_zero(mem_ctx, qt_len);
> +	if (qbuf.data == NULL) {
> +		status = NT_STATUS_NO_MEMORY;
> +		goto fail;
> +	}
> +
> +	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;
> +		/* Fixme: do we have a standard "round-up" primitive? */
> +		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);
> +
> +		/* 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);
> +	}
> +
> +	/* 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
> +	 */
> +	qbuf.length = qt_len;
> +	*outbuf = qbuf;
> +	qbuf = data_blob_null;
> +	status = NT_STATUS_OK;
> +
> +	if (end_ptr) {
> +		*end_ptr = qt_list;
> +	}
> +
> +fail:
> +	data_blob_free(&qbuf);
> +
> +	return status;
> +}
> +
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt)
>  {
> @@ -255,39 +367,32 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  }
>  
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -			    SMB_NTQUOTA_STRUCT *pqt)
> +			    SMB_NTQUOTA_LIST *qtl)
>  {
>  	uint16_t setup[1];
>  	uint8_t params[2];
> -	uint8_t data[112];
> -	unsigned int sid_len;	
> -	NTSTATUS status;
> +	DATA_BLOB data = data_blob_null;
> +	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
>  
> -	memset(data,'\0',112);
> -
> -	if (!cli||!pqt) {
> +	if (!cli || !qtl) {
>  		smb_panic("cli_set_user_quota() called with NULL Pointer!");
>  	}
>  
> +	status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
>  	SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
>  
>  	SSVAL(params,0,quota_fnum);
>  
> -	sid_len = ndr_size_dom_sid(&pqt->sid, 0);
> -	SIVAL(data,0,0);
> -	SIVAL(data,4,sid_len);
> -	SBIG_UINT(data, 8,(uint64_t)0);
> -	SBIG_UINT(data,16,pqt->usedspace);
> -	SBIG_UINT(data,24,pqt->softlim);
> -	SBIG_UINT(data,32,pqt->hardlim);
> -	sid_linearize(data+40, sid_len, &pqt->sid);
> -
>  	status = cli_trans(talloc_tos(), cli, SMBnttrans,
>  			   NULL, -1, /* name, fid */
>  			   NT_TRANSACT_SET_USER_QUOTA, 0,
>  			   setup, 1, 0, /* setup */
>  			   params, 2, 0, /* params */
> -			   data, 112, 0, /* data */
> +			   data.data, data.length, 0, /* data */
>  			   NULL,		/* recv_flags2 */
>  			   NULL, 0, NULL,	/* rsetup */
>  			   NULL, 0, NULL,	/* rparams */
> @@ -298,6 +403,8 @@ NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
>  			  nt_errstr(status)));
>  	}
>  
> +cleanup:
> +	data_blob_free(&data);
>  	return status;
>  }
>  
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index 075df01..a2b22a6 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -762,6 +762,8 @@ int cli_printjob_del(struct cli_state *cli, int job);
>  
>  NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum);
>  void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list);
> +bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx, SMB_NTQUOTA_STRUCT *pqt,
> +				SMB_NTQUOTA_LIST **pqt_list);
>  bool parse_user_quota_record(const uint8_t *rdata, unsigned int rdata_count,
>  			     unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_size,
> @@ -769,10 +771,13 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata, uint32_t curdata_size,
>  			       SMB_NTQUOTA_LIST **pqt_list);
>  NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, unsigned int rdata_count,
>  			       SMB_NTQUOTA_STRUCT *pqt);
> +NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, uint32_t maxlen,
> +				 TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf,
> +				 SMB_NTQUOTA_LIST **end_ptr);
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -			    SMB_NTQUOTA_STRUCT *pqt);
> +			    SMB_NTQUOTA_LIST *qtl);
>  NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
>  			     SMB_NTQUOTA_LIST **pqt_list);
>  NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
> diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
> index cda5f92..c122457 100644
> --- a/source3/utils/smbcquotas.c
> +++ b/source3/utils/smbcquotas.c
> @@ -339,6 +339,7 @@ static int do_quota(struct cli_state *cli,
>  	uint32_t fs_attrs = 0;
>  	uint16_t quota_fnum = 0;
>  	SMB_NTQUOTA_LIST *qtl = NULL;
> +	TALLOC_CTX *qtl_ctx = NULL;
>  	SMB_NTQUOTA_STRUCT qt;
>  	NTSTATUS status;
>  
> @@ -386,8 +387,21 @@ static int do_quota(struct cli_state *cli,
>  					break;
>  				case QUOTA_SETLIM:
>  					pqt->sid = qt.sid;
> +					if ((qtl_ctx = talloc_init(
> +						 "SMB_USER_QUOTA_SET")) ==
> +					    NULL) {
> +						return -1;
> +					}
> +
> +					if (!add_record_to_ntquota_list(
> +						qtl_ctx, pqt, &qtl)) {
> +						TALLOC_FREE(qtl_ctx);
> +						return -1;
> +					}
> +
>  					status = cli_set_user_quota(
> -						cli, quota_fnum, pqt);
> +					    cli, quota_fnum, qtl);
> +					free_ntquota_list(&qtl);
>  					if (!NT_STATUS_IS_OK(status)) {
>  						d_printf("%s cli_set_user_quota %s\n",
>  							 nt_errstr(status),
> -- 
> 2.5.5
> 
> 
> From 425114ffe4c0e29637b084b7e6f6601fec67a4c7 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Wed, 21 Sep 2016 19:35:39 +0300
> Subject: [PATCH 16/19] cliquota: support setting user quota via SMB2
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 48 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  2 ++
>  source3/libsmb/cliquota.c      |  4 ++++
>  3 files changed, 54 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index e86d228..c14aeaf 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -2551,6 +2551,54 @@ cleanup:
>  	return status;
>  }
>  
> +/***************************************************************
> + Wrapper that allows SMB2 to set user quota.
> + Synchronous only.
> +***************************************************************/
> +
> +NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, int quota_fnum,
> +				 SMB_NTQUOTA_LIST *qtl)
> +{
> +	NTSTATUS status;
> +	DATA_BLOB inbuf = data_blob_null;
> +	struct smb2_hnd *ph = NULL;
> +	TALLOC_CTX *frame = talloc_stackframe();
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = smb2cli_set_info(cli->conn, cli->timeout, cli->smb2.session,
> +				  cli->smb2.tcon, 4, /* in_info_type */
> +				  0,		     /* in_file_info_class */
> +				  &inbuf,	    /* in_input_buffer */
> +				  0,		     /* in_additional_info */
> +				  ph->fid_persistent, ph->fid_volatile);
> +cleanup:
> +	TALLOC_FREE(frame);
> +
> +	return status;
> +}
> +
>  struct cli_smb2_read_state {
>  	struct tevent_context *ev;
>  	struct cli_state *cli;
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index 0dfa543..8db9b97 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -156,6 +156,8 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
>  				       SMB_NTQUOTA_LIST **pqt_list, bool first);
>  NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  				    SMB_NTQUOTA_STRUCT *pqt);
> +NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, int quota_fnum,
> +				 SMB_NTQUOTA_LIST *qtl);
>  struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
>  				struct tevent_context *ev,
>  				struct cli_state *cli,
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 37c80ca..cfdfc8d 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -378,6 +378,10 @@ NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
>  		smb_panic("cli_set_user_quota() called with NULL Pointer!");
>  	}
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
> +	}
> +
>  	status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
>  	if (!NT_STATUS_IS_OK(status)) {
>  		goto cleanup;
> -- 
> 2.5.5
> 
> 
> From ee859153690e5a509e6f39028f15f27c9231fbe6 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Wed, 21 Sep 2016 23:58:33 +0300
> Subject: [PATCH 17/19] cliquota: factor out building of
>  FILE_FS_CONTROL_INFORMATION
> 
> add a service routine that builds FILE_FS_CONTROL_INFORMATION
> with default quota and flags. This will be reused by SMB2 and
> by server code.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cliquota.c | 63 +++++++++++++++++++++++++++++++++--------------
>  source3/libsmb/proto.h    |  3 +++
>  2 files changed, 48 insertions(+), 18 deletions(-)
> 
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index cfdfc8d..904e271 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -305,6 +305,44 @@ fail:
>  	return status;
>  }
>  
> +NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
> +			       const SMB_NTQUOTA_STRUCT *pqt, DATA_BLOB *blob,
> +			       uint32_t maxlen)
> +{
> +	uint8_t *buf;
> +
> +	if (maxlen > 0 && maxlen < 48) {
> +		return NT_STATUS_BUFFER_TOO_SMALL;
> +	}
> +
> +	*blob = data_blob_talloc_zero(mem_ctx, 48);
> +
> +	if (!blob->data) {
> +		return NT_STATUS_NO_MEMORY;
> +	}
> +
> +	buf = blob->data;
> +
> +	/* Unknown1 24 NULL bytes*/
> +	SBIG_UINT(buf, 0, (uint64_t)0);
> +	SBIG_UINT(buf, 8, (uint64_t)0);
> +	SBIG_UINT(buf, 16, (uint64_t)0);
> +
> +	/* Default Soft Quota 8 bytes */
> +	SBIG_UINT(buf, 24, pqt->softlim);
> +
> +	/* Default Hard Quota 8 bytes */
> +	SBIG_UINT(buf, 32, pqt->hardlim);
> +
> +	/* Quota flag 4 bytes */
> +	SIVAL(buf, 40, pqt->qflags);
> +
> +	/* 4 padding bytes */
> +	SIVAL(buf, 44, 0);
> +
> +	return NT_STATUS_OK;
> +}
> +
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt)
>  {
> @@ -552,40 +590,29 @@ NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  {
>  	uint16_t setup[1];
>  	uint8_t param[4];
> -	uint8_t data[48];
> -	SMB_NTQUOTA_STRUCT qt;
> +	DATA_BLOB data = data_blob_null;
>  	NTSTATUS status;
> -	ZERO_STRUCT(qt);
> -	memset(data,'\0',48);
>  
>  	if (!cli||!pqt) {
>  		smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
>  	}
>  
> +	status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		return status;
> +	}
> +
>  	SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
>  
>  	SSVAL(param,0,quota_fnum);
>  	SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
>  
> -	/* Unknown1 24 NULL bytes*/
> -
> -	/* Default Soft Quota 8 bytes */
> -	SBIG_UINT(data,24,pqt->softlim);
> -
> -	/* Default Hard Quota 8 bytes */
> -	SBIG_UINT(data,32,pqt->hardlim);
> -
> -	/* Quota flag 2 bytes */
> -	SSVAL(data,40,pqt->qflags);
> -
> -	/* Unknown3 6 NULL bytes */
> -
>  	status = cli_trans(talloc_tos(), cli, SMBtrans2,
>  			   NULL, -1, /* name, fid */
>  			   0, 0,     /* function, flags */
>  			   setup, 1, 0, /* setup */
>  			   param, 4, 0, /* param */
> -			   data, 48, 0, /* data */
> +			   data.data, data.length, 0, /* data */
>  			   NULL,	 /* recv_flags2 */
>  			   NULL, 0, NULL, /* rsetup */
>  			   NULL, 0, NULL, /* rparam */
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index a2b22a6..08d3829 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -774,6 +774,9 @@ NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, unsigned int rdata_count,
>  NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, uint32_t maxlen,
>  				 TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf,
>  				 SMB_NTQUOTA_LIST **end_ptr);
> +NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
> +			       const SMB_NTQUOTA_STRUCT *pqt, DATA_BLOB *blob,
> +			       uint32_t maxlen);
>  NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
>  			    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
> -- 
> 2.5.5
> 
> 
> From bb9738b4b0d5e1a657e1cc64c7674196e7ffed23 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Thu, 22 Sep 2016 01:03:41 +0300
> Subject: [PATCH 18/19] cliquota: support setting file system quota via SMB2
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  source3/libsmb/cli_smb2_fnum.c | 43 ++++++++++++++++++++++++++++++++++++++++++
>  source3/libsmb/cli_smb2_fnum.h |  2 ++
>  source3/libsmb/cliquota.c      |  4 ++++
>  3 files changed, 49 insertions(+)
> 
> diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
> index c14aeaf..d3e4ad2 100644
> --- a/source3/libsmb/cli_smb2_fnum.c
> +++ b/source3/libsmb/cli_smb2_fnum.c
> @@ -2599,6 +2599,49 @@ cleanup:
>  	return status;
>  }
>  
> +NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
> +				    SMB_NTQUOTA_STRUCT *pqt)
> +{
> +	NTSTATUS status;
> +	DATA_BLOB inbuf = data_blob_null;
> +	struct smb2_hnd *ph = NULL;
> +	TALLOC_CTX *frame = talloc_stackframe();
> +
> +	if (smbXcli_conn_has_async_calls(cli->conn)) {
> +		/*
> +		 * Can't use sync call while an async call is in flight
> +		 */
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) {
> +		status = NT_STATUS_INVALID_PARAMETER;
> +		goto cleanup;
> +	}
> +
> +	status = map_fnum_to_smb2_handle(cli, quota_fnum, &ph);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		goto cleanup;
> +	}
> +
> +	status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0);
> +	if (!NT_STATUS_IS_OK(status)) {
> +		return status;
> +	}
> +
> +	status = smb2cli_set_info(
> +	    cli->conn, cli->timeout, cli->smb2.session, cli->smb2.tcon,
> +	    2,				     /* in_info_type */
> +	    SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */
> +	    &inbuf,			     /* in_input_buffer */
> +	    0,				     /* in_additional_info */
> +	    ph->fid_persistent, ph->fid_volatile);
> +cleanup:
> +	TALLOC_FREE(frame);
> +	return status;
> +}
> +
>  struct cli_smb2_read_state {
>  	struct tevent_context *ev;
>  	struct cli_state *cli;
> diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h
> index 8db9b97..6421cad 100644
> --- a/source3/libsmb/cli_smb2_fnum.h
> +++ b/source3/libsmb/cli_smb2_fnum.h
> @@ -158,6 +158,8 @@ NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  				    SMB_NTQUOTA_STRUCT *pqt);
>  NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, int quota_fnum,
>  				 SMB_NTQUOTA_LIST *qtl);
> +NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
> +				    SMB_NTQUOTA_STRUCT *pqt);
>  struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx,
>  				struct tevent_context *ev,
>  				struct cli_state *cli,
> diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
> index 904e271..0510729 100644
> --- a/source3/libsmb/cliquota.c
> +++ b/source3/libsmb/cliquota.c
> @@ -597,6 +597,10 @@ NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
>  		smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
>  	}
>  
> +	if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> +		return cli_smb2_set_fs_quota_info(cli, quota_fnum, pqt);
> +	}
> +
>  	status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
>  	if (!NT_STATUS_IS_OK(status)) {
>  		return status;
> -- 
> 2.5.5
> 
> 
> From e3aa6bdd89b0e5fdc5d57e39987bf2ee92a14332 Mon Sep 17 00:00:00 2001
> From: Uri Simchoni <uri at samba.org>
> Date: Tue, 20 Sep 2016 18:51:00 +0300
> Subject: [PATCH 19/19] smbcquotas: add -m option
> 
> Add the "standard" -m command line option that controls max
> client protocol.
> 
> Signed-off-by: Uri Simchoni <uri at samba.org>
> ---
>  docs-xml/manpages/smbcquotas.1.xml | 12 ++++++++++++
>  source3/utils/smbcquotas.c         |  6 ++++++
>  2 files changed, 18 insertions(+)
> 
> diff --git a/docs-xml/manpages/smbcquotas.1.xml b/docs-xml/manpages/smbcquotas.1.xml
> index bbdb9fd..bff24a4 100644
> --- a/docs-xml/manpages/smbcquotas.1.xml
> +++ b/docs-xml/manpages/smbcquotas.1.xml
> @@ -34,6 +34,7 @@
>  		<arg choice="opt">-V</arg>
>  
>  		<arg choice="opt">-U username</arg>
> +		<arg choice="opt">-m|--max-protocol LEVEL</arg>
>  		<arg choice="opt">-N</arg>
>  		<arg choice="opt">-k</arg>
>  		<arg choice="opt">-A</arg>
> @@ -96,6 +97,17 @@
>  		</varlistentry>
>  
>  		<varlistentry>
> +		<term>-m|--max-protocol PROTOCOL_NAME</term>
> +		<listitem><para>This allows the user to select the
> +		highest SMB protocol level that smbcquotas will use to
> +		connect to the server. By default this is set to
> +		NT1, which is the highest available SMB1 protocol.
> +		To connect using SMB2 or SMB3 protocol, use the
> +		strings SMB2 or SMB3 respectively.
> +		</para></listitem>
> +		</varlistentry>
> +
> +		<varlistentry>
>  		<term>-t|--test-args</term>
>  		<listitem><para>
>  		Don't actually do anything, only validate the correctness of the arguments.
> diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
> index c122457..06ecea1 100644
> --- a/source3/utils/smbcquotas.c
> +++ b/source3/utils/smbcquotas.c
> @@ -598,6 +598,8 @@ FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" },
>  		{ "numeric", 'n', POPT_ARG_NONE, NULL, 'n', "Don't resolve sids or limits to names" },
>  		{ "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "be verbose" },
>  		{ "test-args", 't', POPT_ARG_NONE, NULL, 't', "Test arguments"},
> +		{"max-protocol", 'm', POPT_ARG_STRING, NULL, 'm',
> +		 "Set the max protocol level", "LEVEL"},
>  		POPT_COMMON_SAMBA
>  		POPT_COMMON_CREDENTIALS
>  		{ NULL }
> @@ -679,6 +681,10 @@ FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" },
>  			}
>  			todo = SET_QUOTA;
>  			break;
> +		case 'm':
> +			lp_set_cmdline("client max protocol",
> +				       poptGetOptArg(pc));
> +			break;
>  		}
>  	}
>  
> -- 
> 2.5.5
> 




More information about the samba-technical mailing list