[PATCHES] SMB2 client quota support

Uri Simchoni uri at samba.org
Sun Oct 2 17:14:27 UTC 2016


Attached v2 of the patch.
In addition to addressing Jeremy's comments, it re-formats all new /
modified function declarations according to recommended style, and
assigns some patches to their individual bug (instead of piling
semi-related patches on the same bug).

Thanks,
Uri.

On 09/26/2016 11:05 PM, Jeremy Allison wrote:
> On Thu, Sep 22, 2016 at 08:09:18PM +0300, Uri Simchoni wrote:
>> Hi,
>>
> 
> 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)
> 
Me too. Fixed. (I delegated formatting to a computer because overall it
does it much better than I do, so I accepted this reformatting, but
following your comment I fixed the clang-format file)

I re-formatted the whole patch set, so function declarations and
definitions are now following the preferred style.

> 
> 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 :-).
> 

Done.

> 3). [PATCH 10/19] cliquota: some security hardening
> 
> +       if (*offset != 0 && *offset < 40 + sid_len) {
> +               return false;
> +       }
> 
> wrap check on 40 + sid_len please !
> 

Done.

> 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.
> 
Done.
> 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 ?
> 
I believe we should, and plan to do it when I get to the server side.
However, quota listing in smbd is pretty much POC level right now IMHO.
The code loops over all users (using getpwent()) and checks their quota
- this won't work with AD member setups, and if it did, it wouldn't
scale. A proper implementation should delegate quota listing to VFS, as
a separate operation, with only the default doing what's done today.

> Jeremy.
> 

-------------- next part --------------
From e01b64883285041978344bfbf06e68a8f348693f 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 v2 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 b2a8eee2833cfc1504dcb9bdf43ba175d012f31e 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 v2 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 68342a486d227c6c2ef700e80d1476ad86792db6 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 v2 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=12307

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 35f2b7ab8d3ace25e44aed64a417372fb66f32c7 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 v2 04/19] cliquota: fix param count when setting fs quota

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12288

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 14bbcd6039112e9c851431537fa3385ca33c0eee 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 v2 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 7c436fd7800534acc2a0bf2b08969fd0ab83fbc2 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 v2 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 73d207d81a5997133b8e6c6d3572855fe342c760 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 v2 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 | 8 ++++----
 source3/libsmb/proto.h    | 4 ++++
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index ce48257..3091efb 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -46,10 +46,10 @@ 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..ecfb0a3 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -762,6 +762,10 @@ 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 2ea18f0d4d7ff2415ec713eb6adeba9b62ec33cc 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 v2 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 | 86 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/cli_smb2_fnum.h |  3 ++
 source3/libsmb/cliquota.c      |  5 +++
 3 files changed, 94 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index a998d6f..a841f4c 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,91 @@ 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 */
+	if (8 + sid_len < 8) {
+		status = NT_STATUS_INVALID_PARAMETER;
+		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 */
+				    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..93d7529 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -149,6 +149,9 @@ 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 3091efb..1f89176 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)
 {
@@ -118,6 +119,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 e1d34d5342c546eb1ba3c239bc104a6b65895c42 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 v2 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 | 137 ++++++++++++++++++----------------------------
 1 file changed, 52 insertions(+), 85 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index 1f89176..84028bb 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -210,8 +210,11 @@ 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];
@@ -220,19 +223,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);
@@ -250,20 +250,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;
@@ -274,19 +268,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));
@@ -295,70 +288,44 @@ 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 eb16533dedfdefceb6c1a4af948b4f701fa06a14 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 v2 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 | 33 +++++++++++++++++++++++++++++----
 1 file changed, 29 insertions(+), 4 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index 84028bb..e4589c4 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -73,11 +73,18 @@ bool parse_user_quota_record(const uint8_t *rdata,
 
 	/* sid len */
 	sid_len = IVAL(rdata,4);
+	if (40 + sid_len < 40) {
+		return false;
+	}
 
 	if (rdata_count < 40+sid_len) {
 		return False;		
 	}
 
+	if (*offset != 0 && *offset < 40 + sid_len) {
+		return false;
+	}
+
 	/* unknown 8 bytes in pdata 
 	 * maybe its the change time in NTTIME
 	 */
@@ -260,10 +267,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)) {
@@ -286,6 +292,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 cc8698239cff20917ad4e6ef1f0a1b1c0195b048 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 v2 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 | 119 +++++++++++++++++++++++++---------------------
 source3/libsmb/proto.h    |   4 ++
 2 files changed, 68 insertions(+), 55 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index e4589c4..19c2e7e 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -109,6 +109,65 @@ bool parse_user_quota_record(const uint8_t *rdata,
 	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)
 {
@@ -227,11 +286,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;
@@ -255,64 +309,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 ecfb0a3..85cf44a 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -766,6 +766,10 @@ 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 c7879ed2183081c9a01973fba248ba3b4e48c5fc 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 v2 12/19] cliquota: implement quota listing in SMB2

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/libsmb/cli_smb2_fnum.c | 73 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/cli_smb2_fnum.h |  5 +++
 source3/libsmb/cliquota.c      |  5 +++
 3 files changed, 83 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index a841f4c..3f5629c 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2433,6 +2433,79 @@ 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 93d7529..b39bfed 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -152,6 +152,11 @@ NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli,
 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 19c2e7e..ff7e6e6 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -290,6 +290,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 680ab0ed60422a270d7c65622224b560727937bf 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 v2 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 | 51 +++++++++++++++++++++++++++++++----------------
 source3/libsmb/proto.h    |  3 +++
 2 files changed, 37 insertions(+), 17 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index ff7e6e6..cec378f 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -168,6 +168,39 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 	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)
 {
@@ -376,11 +409,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!");
 	}
@@ -406,20 +436,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 85cf44a..eaa9c8e 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -770,6 +770,9 @@ 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 bed45a2196f51c3ddea6bb9640b09b0c476f5c7c 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 v2 14/19] cliquota: support getting fs quota by SMB2

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/libsmb/cli_smb2_fnum.c | 53 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/cli_smb2_fnum.h |  3 +++
 source3/libsmb/cliquota.c      |  4 ++++
 3 files changed, 60 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 3f5629c..1be9381 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2506,6 +2506,59 @@ 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 b39bfed..7fd97ec 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -157,6 +157,9 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
 				       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 cec378f..9a80eef 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -415,6 +415,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 acde5e793c186cd2634e497f25e442199a73b136 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 v2 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  | 190 ++++++++++++++++++++++++++++++++++++---------
 source3/libsmb/proto.h     |  13 +++-
 source3/utils/smbcquotas.c |  16 +++-
 3 files changed, 180 insertions(+), 39 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index 9a80eef..f11d9a2 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -47,6 +47,29 @@ 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,
@@ -117,7 +140,6 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 	NTSTATUS status = NT_STATUS_OK;
 	unsigned offset;
 	SMB_NTQUOTA_STRUCT qt;
-	SMB_NTQUOTA_LIST *tmp_list_ent;
 
 	while (true) {
 		ZERO_STRUCT(qt);
@@ -128,23 +150,6 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 			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;
@@ -157,6 +162,11 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 			break;
 		}
 
+		if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) {
+			status = NT_STATUS_NO_MEMORY;
+			break;
+		}
+
 		curdata += offset;
 		curdata_count -= offset;
 
@@ -201,6 +211,119 @@ NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
 	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) {
+
+		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;
+
+		if (qt_len + entry_len < qt_len) {
+			status = NT_STATUS_INVALID_PARAMETER;
+			goto fail;
+		}
+		qt_len += entry_len;
+	}
+
+	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;
+		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)
 {
@@ -262,40 +385,33 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
 	return status;
 }
 
-NTSTATUS cli_set_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_LIST *qtl)
 {
 	uint16_t setup[1];
 	uint8_t params[2];
-	uint8_t data[112];
-	unsigned int sid_len;	
-	NTSTATUS status;
-
-	memset(data,'\0',112);
+	DATA_BLOB data = data_blob_null;
+	NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
 
-	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 */
@@ -306,6 +422,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 eaa9c8e..1372dee 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -766,6 +766,9 @@ bool parse_user_quota_record(const uint8_t *rdata,
 			     unsigned int rdata_count,
 			     unsigned int *offset,
 			     SMB_NTQUOTA_STRUCT *pqt);
+bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx,
+				SMB_NTQUOTA_STRUCT *pqt,
+				SMB_NTQUOTA_LIST **pqt_list);
 NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 			       uint32_t curdata_size,
 			       TALLOC_CTX *mem_ctx,
@@ -773,10 +776,16 @@ NTSTATUS parse_user_quota_list(const uint8_t *curdata,
 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);
+NTSTATUS cli_set_user_quota(struct cli_state *cli,
+			    int quota_fnum,
+			    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 d45336f88f851284d1132e90ede10c05df2f7462 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 v2 16/19] cliquota: support setting user quota via SMB2

Signed-off-by: Uri Simchoni <uri at samba.org>
---
 source3/libsmb/cli_smb2_fnum.c | 49 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/cli_smb2_fnum.h |  3 +++
 source3/libsmb/cliquota.c      |  4 ++++
 3 files changed, 56 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 1be9381..9ee4dae 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2559,6 +2559,55 @@ 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 7fd97ec..7c618d1 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -160,6 +160,9 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
 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 f11d9a2..57bffe2 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -397,6 +397,10 @@ cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
 		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 715b1e68e1065ac0c550fd41cad7a78f1f509dbf 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 v2 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 | 64 ++++++++++++++++++++++++++++++++++-------------
 source3/libsmb/proto.h    |  4 +++
 2 files changed, 50 insertions(+), 18 deletions(-)

diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index 57bffe2..09c3d4d 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -324,6 +324,45 @@ 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)
 {
@@ -573,40 +612,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 1372dee..6da95d3 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -781,6 +781,10 @@ NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
 				 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,
-- 
2.5.5


From e27f1e910e57d876143f10a001b7f04c09ff74bb 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 v2 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 | 44 ++++++++++++++++++++++++++++++++++++++++++
 source3/libsmb/cli_smb2_fnum.h |  3 +++
 source3/libsmb/cliquota.c      |  4 ++++
 3 files changed, 51 insertions(+)

diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 9ee4dae..cf00f60 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -2608,6 +2608,50 @@ 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 7c618d1..3289f7e 100644
--- a/source3/libsmb/cli_smb2_fnum.h
+++ b/source3/libsmb/cli_smb2_fnum.h
@@ -163,6 +163,9 @@ NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli,
 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 09c3d4d..e22ccdd 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -619,6 +619,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 6282bfa7302aec6764d96303fb705cc37dc7600d 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 v2 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