Quota patches
Jeremy Allison
jra at samba.org
Fri Jul 27 18:05:47 UTC 2018
On Fri, Jul 27, 2018 at 01:51:10PM +0100, Noel Power via samba-technical wrote:
> On 27/07/18 13:20, David Disseldorp wrote:
> > Hi Noel,
> >
> > I didn't get a chance to review the full patchset, but one minor
> > comment...
> >
> > On Thu, 26 Jul 2018 16:31:37 +0100, Noel Power via samba-technical wrote:
> >
> >> diff --git a/librpc/idl/wscript_build b/librpc/idl/wscript_build
> >> index 75eba7d54a5..57e75363f72 100644
> >> --- a/librpc/idl/wscript_build
> >> +++ b/librpc/idl/wscript_build
> >> @@ -12,6 +12,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
> >> drsblobs.idl efs.idl frstrans.idl mgmt.idl netlogon.idl
> >> notify.idl
> >> smb2_lease_struct.idl
> >> + quota.idl
> >> policyagent.idl scerpc.idl svcctl.idl wkssvc.idl eventlog6.idl backupkey.idl
> >> fsrvp.idl bkupblobs.idl fscc.idl frsblobs.idl witness.idl clusapi.idl
> >> mdssvc.idl winspool.idl''',
> > You're only making use of the ndr push/pull functionality here right? If
> > so, please move quota.idl down to the options='--header --ndr-parser'
> > section so that we're not generating more unused server/client/python
> > code.
> good call, I will do that for the next round
OK, here is a version I'm OK with.
To be honest, I'd love to see the code split up into
micro-patches with get/set done separately, but to
quote one of my favorite James Bond villains "It's
late, I'm tired, and there's so much left to do.." :-).
Noel, if this works for you please push !
You can see the differences between your patch and
this by doing a diff.
Jeremy.
-------------- next part --------------
From 933ca13a8eb8f1c946b0739fcc6fc4d309e278b1 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 22 Mar 2017 14:53:22 +0000
Subject: [PATCH 01/12] s3/lib: Fix misleading typo in debug message
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/lib/sysquotas.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source3/lib/sysquotas.c b/source3/lib/sysquotas.c
index eef87beafe0..9b2d37b8375 100644
--- a/source3/lib/sysquotas.c
+++ b/source3/lib/sysquotas.c
@@ -418,7 +418,7 @@ static int command_set_quota(const char *path, enum SMB_QUOTA_TYPE qtype, unid_t
return -1;
}
- DEBUG (3, ("get_quota: Running command %s\n", syscmd));
+ DBG_NOTICE("set_quota: Running command %s\n", syscmd);
lines = file_lines_pload(talloc_tos(), syscmd, NULL);
SAFE_FREE(syscmd);
--
2.18.0.345.g5c9ce644c3-goog
From 86a7bbcc1e1e342be08827ad65da102251e85f14 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 28 Feb 2017 15:04:16 +0000
Subject: [PATCH 02/12] s3/libsmb: Avoid potential smbpanic calling
parse_user_quota_list.
Calling parse_user_quota_list with a NULL buffer can cause a panic, while
this shouldn't happen, I managed to trigger this with an early implementation
of SMB2 quota support in smbd which didn't pass back NT_STATUS_NO_MORE_ENTRIES
when handling a SMB2_0_INFO_QUOTA GETINFO message.
OTHOH the Windows client handled the same situation gracefully.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/cli_smb2_fnum.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 4b9871c54df..3546ae422ec 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -3047,6 +3047,14 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
ph->fid_persistent, ph->fid_volatile, frame,
&outbuf);
+ /*
+ * safeguard against panic from calling parse_user_quota_list with
+ * NULL buffer
+ */
+ if (NT_STATUS_IS_OK(status) && outbuf.length == 0) {
+ status = NT_STATUS_NO_MORE_ENTRIES;
+ }
+
if (!NT_STATUS_IS_OK(status)) {
goto cleanup;
}
--
2.18.0.345.g5c9ce644c3-goog
From 6d7799ce4d375f14d204d53f2ae370459b295648 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 28 Feb 2017 11:36:47 +0000
Subject: [PATCH 03/12] s3/smbd: Don't stat when doing a quota operation (as
it's a fake file)
calling SMB_VFS_STAT on the quota fake file fails and caused
FS_INFO/FileFsControlInfo request to error out early, in turn stopped a
Win8.1 client from proceeding with quota queries.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/trans2.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c
index b4a481fbf86..0ec20fe6337 100644
--- a/source3/smbd/trans2.c
+++ b/source3/smbd/trans2.c
@@ -3475,7 +3475,8 @@ NTSTATUS smbd_do_qfsinfo(struct smbXsrv_connection *xconn,
ZERO_STRUCT(smb_fname);
smb_fname.base_name = discard_const_p(char, filename);
- if(SMB_VFS_STAT(conn, &smb_fname) != 0) {
+ if(info_level != SMB_FS_QUOTA_INFORMATION
+ && SMB_VFS_STAT(conn, &smb_fname) != 0) {
DEBUG(2,("stat of . failed (%s)\n", strerror(errno)));
return map_nt_error_from_unix(errno);
}
--
2.18.0.345.g5c9ce644c3-goog
From 24c0862e20ae05d6ef9409fa83ee3f9e27240743 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 2 Mar 2017 09:20:24 +0000
Subject: [PATCH 04/12] librpc/idl Add some query [getset]info quota related
structures
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
librpc/idl/quota.idl | 54 ++++++++++++++++++++++++++++++++++++++++
librpc/idl/wscript_build | 1 +
librpc/wscript_build | 5 ++++
3 files changed, 60 insertions(+)
create mode 100644 librpc/idl/quota.idl
diff --git a/librpc/idl/quota.idl b/librpc/idl/quota.idl
new file mode 100644
index 00000000000..7c4d00acfcb
--- /dev/null
+++ b/librpc/idl/quota.idl
@@ -0,0 +1,54 @@
+#include "idl_types.h"
+
+import "security.idl";
+
+[
+ pointer_default(unique)
+]
+
+interface file_quota {
+
+ /* MS-FSCC 2.4.33.1 */
+ typedef [public] struct {
+ uint32 next_entry_offset;
+ uint32 sid_length;
+ dom_sid sid;
+ } file_get_quota_info;
+
+ /* MS-FSCC 2.4.33 */
+ typedef [public] struct {
+ uint32 next_entry_offset;
+ uint32 sid_length;
+ hyper change_time;
+ hyper quota_used;
+ hyper quota_threshold;
+ hyper quota_limit;
+ dom_sid sid;
+ } file_quota_information;
+}
+
+interface smb2_query_quoata
+{
+ /* MS-SMB2 2.2.37.1 */
+ typedef [public] struct {
+ uint8 return_single;
+ uint8 restart_scan;
+ uint16 reserved;
+ uint32 sid_list_length;
+ uint32 start_sid_length;
+ uint32 start_sid_offset;
+ } smb2_query_quota_info;
+}
+
+interface smb1_nt_transact_query_quota
+{
+ /* MS-SMB 2.2.7.5.1 */
+ typedef [public] struct {
+ uint16 fid;
+ uint8 return_single_entry;
+ uint8 restart_scan;
+ uint32 sid_list_length;
+ uint32 start_sid_length;
+ uint32 start_sid_offset;
+ } nttrans_query_quota_params;
+}
diff --git a/librpc/idl/wscript_build b/librpc/idl/wscript_build
index 75eba7d54a5..f1588cd18fb 100644
--- a/librpc/idl/wscript_build
+++ b/librpc/idl/wscript_build
@@ -38,6 +38,7 @@ bld.SAMBA_PIDL_LIST('PIDL',
fsrvp_state.idl
cab.idl
nfs4acl.idl
+ quota.idl
''',
options='--header --ndr-parser',
output_dir='../gen_ndr')
diff --git a/librpc/wscript_build b/librpc/wscript_build
index 36414fbddf4..824c0f9828e 100644
--- a/librpc/wscript_build
+++ b/librpc/wscript_build
@@ -399,6 +399,11 @@ bld.SAMBA_SUBSYSTEM('NDR_SMB2_LEASE_STRUCT',
public_headers='gen_ndr/smb2_lease_struct.h'
)
+bld.SAMBA_SUBSYSTEM('NDR_QUOTA',
+ source='gen_ndr/ndr_quota.c',
+ public_deps='ndr',
+ )
+
bld.SAMBA_SUBSYSTEM('NDR_SCHANNEL',
source='ndr/ndr_schannel.c gen_ndr/ndr_schannel.c',
public_deps='ndr ndr_nbt'
--
2.18.0.345.g5c9ce644c3-goog
From 2b7daf87b8ecc546d563bb6031a944f4a399d323 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 26 May 2017 15:01:17 +0100
Subject: [PATCH 05/12] s3/libsmb: adjust smb1 cli code to use idl structs and
ndr push/pull funcs.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/cli_smb2_fnum.c | 2 +-
source3/libsmb/cliquota.c | 366 +++++++++++++++++----------------
source3/libsmb/proto.h | 6 +-
source3/wscript_build | 1 +
4 files changed, 191 insertions(+), 184 deletions(-)
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 3546ae422ec..9647d97d2d7 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -3156,7 +3156,7 @@ NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli,
goto cleanup;
}
- status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL);
+ status = build_user_quota_buffer(qtl, talloc_tos(), &inbuf, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup;
}
diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index e22ccdd1931..3f5c39bb0d9 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -24,6 +24,7 @@
#include "../libcli/security/security.h"
#include "trans2.h"
#include "../libcli/smb/smbXcli_base.h"
+#include "librpc/gen_ndr/ndr_quota.h"
NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
{
@@ -75,61 +76,39 @@ bool parse_user_quota_record(const uint8_t *rdata,
unsigned int *offset,
SMB_NTQUOTA_STRUCT *pqt)
{
- int sid_len;
- SMB_NTQUOTA_STRUCT qt;
+ struct file_quota_information info = {0};
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB blob;
+ enum ndr_err_code err;
+ bool result = false;
- ZERO_STRUCT(qt);
+ blob.data = discard_const_p(uint8_t, rdata);
+ blob.length = rdata_count;
+ err = ndr_pull_struct_blob(
+ &blob,
+ frame,
+ &info,
+ (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
- if (!rdata||!offset||!pqt) {
- smb_panic("parse_quota_record: called with NULL POINTER!");
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ goto out;
}
- if (rdata_count < 40) {
- return False;
- }
+ *offset = info.next_entry_offset;
- /* offset to next quota record.
- * 4 bytes IVAL(rdata,0)
- * unused here...
- */
- *offset = IVAL(rdata,0);
-
- /* sid len */
- sid_len = IVAL(rdata,4);
- if (40 + sid_len < 40) {
- return false;
- }
+ ZERO_STRUCTP(pqt);
+ pqt->usedspace = info.quota_used;
- 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
- */
+ pqt->softlim = info.quota_threshold;
- /* the used space 8 bytes (uint64_t)*/
- qt.usedspace = BVAL(rdata,16);
+ pqt->hardlim = info.quota_limit;
- /* the soft quotas 8 bytes (uint64_t)*/
- qt.softlim = BVAL(rdata,24);
-
- /* the hard quotas 8 bytes (uint64_t)*/
- qt.hardlim = BVAL(rdata,32);
-
- if (!sid_parse(rdata+40,sid_len,&qt.sid)) {
- return false;
- }
-
- qt.qtype = SMB_USER_QUOTA_TYPE;
-
- *pqt = qt;
-
- return True;
+ pqt->qtype = SMB_USER_QUOTA_TYPE;
+ pqt->sid = info.sid;
+ result = true;
+out:
+ TALLOC_FREE(frame);
+ return result;
}
NTSTATUS parse_user_quota_list(const uint8_t *curdata,
@@ -212,116 +191,15 @@ NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
}
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;
+ return fill_quota_buffer(mem_ctx,
+ qt_list,
+ false,
+ outbuf,
+ end_ptr);
}
NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
@@ -367,43 +245,73 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
SMB_NTQUOTA_STRUCT *pqt)
{
uint16_t setup[1];
- uint8_t params[16];
- unsigned int data_len;
- uint8_t data[SID_MAX_SIZE+8];
- uint8_t *rparam, *rdata;
+ uint8_t *rparam = NULL, *rdata = NULL;
uint32_t rparam_count, rdata_count;
unsigned int sid_len;
unsigned int offset;
+ struct nttrans_query_quota_params get_quota = {0};
+ struct file_get_quota_info info = {0};
+ enum ndr_err_code err;
+ struct ndr_push *ndr_push = NULL;
NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ DATA_BLOB data_blob = data_blob_null;
if (!cli||!pqt) {
smb_panic("cli_get_user_quota() called with NULL Pointer!");
}
if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+ TALLOC_FREE(frame);
return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
}
- SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
-
- SSVAL(params, 0,quota_fnum);
- SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
- SIVAL(params, 4,0x00000024);
- SIVAL(params, 8,0x00000000);
- SIVAL(params,12,0x00000024);
+ get_quota.fid = quota_fnum;
+ get_quota.return_single_entry = 1;
+ get_quota.restart_scan = 0;
sid_len = ndr_size_dom_sid(&pqt->sid, 0);
- data_len = sid_len+8;
- SIVAL(data, 0, 0x00000000);
- SIVAL(data, 4, sid_len);
- sid_linearize(data+8, sid_len, &pqt->sid);
+
+ info.next_entry_offset = 0;
+ info.sid_length = sid_len;
+ info.sid = pqt->sid;
+
+ err = ndr_push_struct_blob(
+ &data_blob,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ get_quota.sid_list_length = data_blob.length;
+ get_quota.start_sid_offset = data_blob.length;
+
+ ndr_push = ndr_push_init_ctx(frame);
+
+ if (!ndr_push) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ err = ndr_push_nttrans_query_quota_params(ndr_push,
+ NDR_SCALARS | NDR_BUFFERS,
+ &get_quota);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
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 */
- data, data_len, 112, /* data */
+ ndr_push->data, ndr_push->offset, 4, /* params */
+ data_blob.data, data_blob.length, 112, /* data */
NULL, /* recv_flags2 */
NULL, 0, NULL, /* rsetup */
&rparam, 4, &rparam_count,
@@ -411,7 +319,7 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
nt_errstr(status)));
- return status;
+ goto out;
}
if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
@@ -419,8 +327,10 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
}
+out:
TALLOC_FREE(rparam);
TALLOC_FREE(rdata);
+ TALLOC_FREE(frame);
return status;
}
@@ -440,9 +350,17 @@ cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
}
- status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
+ status = build_user_quota_buffer(qtl, talloc_tos(), &data, NULL);
if (!NT_STATUS_IS_OK(status)) {
- goto cleanup;
+ /*
+ * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow
+ * this status.
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ status = NT_STATUS_OK;
+ } else {
+ goto cleanup;
+ }
}
SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
@@ -477,31 +395,42 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
bool first)
{
uint16_t setup[1];
- uint8_t params[16];
+ DATA_BLOB params_blob = data_blob_null;
uint8_t *rparam=NULL, *rdata=NULL;
uint32_t rparam_count=0, rdata_count=0;
NTSTATUS status;
- uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
- : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
+ struct nttrans_query_quota_params quota_params = {0};
+ enum ndr_err_code err;
+ TALLOC_CTX *frame = NULL;
if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
pqt_list, first);
}
+ frame = talloc_stackframe();
SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
- SSVAL(params, 0,quota_fnum);
- SSVAL(params, 2, op);
- SIVAL(params, 4,0x00000000);
- SIVAL(params, 8,0x00000000);
- SIVAL(params,12,0x00000000);
+ quota_params.fid = quota_fnum;
+ if (first) {
+ quota_params.restart_scan = 1;
+ }
+ err = ndr_push_struct_blob(
+ ¶ms_blob,
+ frame,
+ "a_params,
+ (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto cleanup;
+ }
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 */
+ params_blob.data, params_blob.length, 4, /* params */
NULL, 0, 2048, /* data */
NULL, /* recv_flags2 */
NULL, 0, NULL, /* rsetup */
@@ -524,6 +453,7 @@ static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
cleanup:
TALLOC_FREE(rparam);
TALLOC_FREE(rdata);
+ TALLOC_FREE(frame);
return status;
}
@@ -651,3 +581,75 @@ NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
return status;
}
+
+NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST *qlist,
+ bool return_single,
+ DATA_BLOB *blob,
+ SMB_NTQUOTA_LIST **end_ptr)
+{
+ int ndr_flags = NDR_SCALARS | NDR_BUFFERS;
+ struct ndr_push *qndr = ndr_push_init_ctx(mem_ctx);
+ uint32_t start_offset = 0;
+ uint32_t padding = 0;
+
+ if (qlist == NULL) {
+ /* We must push at least one. */
+ return NT_STATUS_NO_MORE_ENTRIES;
+ }
+ for (;qlist != NULL; qlist = qlist->next) {
+ struct file_quota_information info = {0};
+ enum ndr_err_code err;
+
+ start_offset = qndr->offset;
+
+ info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0);
+ info.sid = qlist->quotas->sid;
+ info.quota_used = qlist->quotas->usedspace;
+ info.quota_threshold = qlist->quotas->softlim;
+ info.quota_limit = qlist->quotas->hardlim;
+
+ err = ndr_push_file_quota_information(qndr,
+ ndr_flags,
+ &info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_DEBUG("Failed to push the quota sid\n");
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* pidl will align to 8 bytes due to 8 byte members*/
+ /* Remember how much align padding we've used. */
+ padding = qndr->offset;
+ ndr_push_align(qndr, 8);
+ padding = qndr->offset - padding;
+
+ /*
+ * Overwrite next_entry_offset for this entry now
+ * we know what it should be. We know we're using
+ * LIBNDR_FLAG_LITTLE_ENDIAN here so we can use
+ * SIVAL.
+ */
+ info.next_entry_offset = qndr->offset - start_offset;
+ SIVAL(qndr->data, start_offset, info.next_entry_offset);
+
+ if (return_single) {
+ break;
+ }
+ }
+
+ if (end_ptr != NULL) {
+ *end_ptr = qlist;
+ }
+
+ /* Remove the padding alignment on the last element pushed. */
+ blob->length = qndr->offset - padding;
+ blob->data = qndr->data;
+
+ /*
+ * Terminate the pushed array by setting next_entry_offset
+ * for the last element to zero.
+ */
+ SIVAL(qndr->data, start_offset, 0);
+ return NT_STATUS_OK;
+}
diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
index d82de56a238..6278e0017c0 100644
--- a/source3/libsmb/proto.h
+++ b/source3/libsmb/proto.h
@@ -793,7 +793,6 @@ 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);
@@ -974,6 +973,11 @@ NTSTATUS cli_readlink(struct cli_state *cli, const char *fname,
TALLOC_CTX *mem_ctx, char **psubstitute_name,
char **pprint_name, uint32_t *pflags);
+NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx,
+ SMB_NTQUOTA_LIST *tmp_list,
+ bool return_single,
+ DATA_BLOB *blob,
+ SMB_NTQUOTA_LIST **end_ptr);
/* The following definitions come from libsmb/passchange.c */
NTSTATUS remote_password_change(const char *remote_machine,
diff --git a/source3/wscript_build b/source3/wscript_build
index b51082913d9..7e38b79c3e2 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -469,6 +469,7 @@ bld.SAMBA3_LIBRARY('libsmb',
LIBTSOCKET
KRBCLIENT
NDR_IOCTL
+ NDR_QUOTA
cli_smb_common
util_cmdline
tevent
--
2.18.0.345.g5c9ce644c3-goog
From 35879a2feb32faa147d9312944f9f5208b1ebc58 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 26 May 2017 15:50:18 +0100
Subject: [PATCH 06/12] s3/libsmb: adjust smb2 code for new idl structs &
generated ndr push/pull funcs.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/cli_smb2_fnum.c | 86 +++++++++++++++++++++++-----------
1 file changed, 58 insertions(+), 28 deletions(-)
diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c
index 9647d97d2d7..106359d32b0 100644
--- a/source3/libsmb/cli_smb2_fnum.c
+++ b/source3/libsmb/cli_smb2_fnum.c
@@ -41,6 +41,7 @@
#include "lib/util_ea.h"
#include "librpc/gen_ndr/ndr_ioctl.h"
#include "ntioctl.h"
+#include "librpc/gen_ndr/ndr_quota.h"
struct smb2_hnd {
uint64_t fid_persistent;
@@ -2910,12 +2911,16 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
{
NTSTATUS status;
DATA_BLOB inbuf = data_blob_null;
+ DATA_BLOB info_blob = data_blob_null;
DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
TALLOC_CTX *frame = talloc_stackframe();
unsigned sid_len;
unsigned int offset;
- uint8_t *buf;
+ struct smb2_query_quota_info query = {0};
+ struct file_get_quota_info info = {0};
+ enum ndr_err_code err;
+ struct ndr_push *ndr_push = NULL;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
@@ -2937,27 +2942,52 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli,
sid_len = ndr_size_dom_sid(&pqt->sid, 0);
- inbuf = data_blob_talloc_zero(frame, 24 + sid_len);
- if (inbuf.data == NULL) {
+ query.return_single = 1;
+ if (sid_len < 0) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ info.next_entry_offset = 0;
+ info.sid_length = sid_len;
+ info.sid = pqt->sid;
+
+ err = ndr_push_struct_blob(
+ &info_blob,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_file_get_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ query.sid_list_length = info_blob.length;
+ ndr_push = ndr_push_init_ctx(frame);
+ if (!ndr_push) {
status = NT_STATUS_NO_MEMORY;
goto fail;
}
- buf = inbuf.data;
+ err = ndr_push_smb2_query_quota_info(ndr_push,
+ NDR_SCALARS | NDR_BUFFERS,
+ &query);
- 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;
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
goto fail;
}
- SIVAL(buf, 4, 8 + sid_len); /* SidListLength */
- SIVAL(buf, 8, 0); /* StartSidLength */
- SIVAL(buf, 12, 0); /* StartSidOffset */
- SIVAL(buf, 16, 0); /* NextEntryOffset */
- SIVAL(buf, 20, sid_len); /* SidLength */
- sid_linearize(buf + 24, sid_len, &pqt->sid);
+
+ err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data,
+ info_blob.length);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+ inbuf.data = ndr_push->data;
+ inbuf.length = ndr_push->offset;
status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
cli->smb2.tcon, 4, /* in_info_type */
@@ -3002,7 +3032,8 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
DATA_BLOB outbuf = data_blob_null;
struct smb2_hnd *ph = NULL;
TALLOC_CTX *frame = talloc_stackframe();
- uint8_t *buf;
+ struct smb2_query_quota_info info = {0};
+ enum ndr_err_code err;
if (smbXcli_conn_has_async_calls(cli->conn)) {
/*
@@ -3022,20 +3053,19 @@ NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli,
goto cleanup;
}
- inbuf = data_blob_talloc_zero(frame, 16);
- if (inbuf.data == NULL) {
- status = NT_STATUS_NO_MEMORY;
- goto cleanup;
- }
- buf = inbuf.data;
+ info.restart_scan = first ? 1 : 0;
- 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 */
+ err = ndr_push_struct_blob(
+ &inbuf,
+ frame,
+ &info,
+ (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto cleanup;
+ }
status = smb2cli_query_info(cli->conn, cli->timeout, cli->smb2.session,
cli->smb2.tcon, 4, /* in_info_type */
--
2.18.0.345.g5c9ce644c3-goog
From 9eba5df302fd96302d3510ca3f56661f2f381958 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 26 May 2017 16:01:53 +0100
Subject: [PATCH 07/12] s3/smbd: adjust smb1 server to use idl structs and
generated ndr push/pull funcs
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/libsmb/cliquota.c | 19 +-
source3/smbd/nttrans.c | 659 ++++++++++++++++++++++----------------
source3/smbd/proto.h | 14 +
3 files changed, 401 insertions(+), 291 deletions(-)
diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c
index 3f5c39bb0d9..a8475905429 100644
--- a/source3/libsmb/cliquota.c
+++ b/source3/libsmb/cliquota.c
@@ -252,10 +252,10 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
struct nttrans_query_quota_params get_quota = {0};
struct file_get_quota_info info = {0};
enum ndr_err_code err;
- struct ndr_push *ndr_push = NULL;
NTSTATUS status;
TALLOC_CTX *frame = talloc_stackframe();
DATA_BLOB data_blob = data_blob_null;
+ DATA_BLOB param_blob = data_blob_null;
if (!cli||!pqt) {
smb_panic("cli_get_user_quota() called with NULL Pointer!");
@@ -290,16 +290,11 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
get_quota.sid_list_length = data_blob.length;
get_quota.start_sid_offset = data_blob.length;
- ndr_push = ndr_push_init_ctx(frame);
-
- if (!ndr_push) {
- status = NT_STATUS_NO_MEMORY;
- goto out;
- }
-
- err = ndr_push_nttrans_query_quota_params(ndr_push,
- NDR_SCALARS | NDR_BUFFERS,
- &get_quota);
+ err = ndr_push_struct_blob(
+ ¶m_blob,
+ frame,
+ &get_quota,
+ (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params);
if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
status = NT_STATUS_INTERNAL_ERROR;
@@ -310,7 +305,7 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
NULL, -1, /* name, fid */
NT_TRANSACT_GET_USER_QUOTA, 0,
setup, 1, 0, /* setup */
- ndr_push->data, ndr_push->offset, 4, /* params */
+ param_blob.data, param_blob.length, 4, /* params */
data_blob.data, data_blob.length, 112, /* data */
NULL, /* recv_flags2 */
NULL, 0, NULL, /* rsetup */
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index ca02dbcc3d9..391caa48199 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -30,6 +30,8 @@
#include "smbprofile.h"
#include "libsmb/libsmb.h"
#include "lib/util_ea.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_security.h"
extern const struct generic_mapping file_generic_mapping;
@@ -2293,285 +2295,390 @@ static void call_nt_transact_ioctl(connection_struct *conn,
#ifdef HAVE_SYS_QUOTAS
-/****************************************************************************
- Reply to get user quota
-****************************************************************************/
-
-static void call_nt_transact_get_user_quota(connection_struct *conn,
- struct smb_request *req,
- uint16_t **ppsetup,
- uint32_t setup_count,
- char **ppparams,
- uint32_t parameter_count,
- char **ppdata,
- uint32_t data_count,
- uint32_t max_data_count)
+static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx,
+ struct files_struct *fsp,
+ SMB_NTQUOTA_HANDLE *qt_handle,
+ struct dom_sid *sids,
+ uint32_t elems)
{
- NTSTATUS nt_status = NT_STATUS_OK;
- char *params = *ppparams;
- char *pdata = *ppdata;
- char *entry;
- int data_len=0,param_len=0;
- int qt_len=0;
- int entry_len = 0;
- files_struct *fsp = NULL;
- uint16_t level = 0;
- size_t sid_len;
- struct dom_sid sid;
- bool start_enum = True;
- SMB_NTQUOTA_STRUCT qt;
- SMB_NTQUOTA_LIST *tmp_list;
- SMB_NTQUOTA_HANDLE *qt_handle = NULL;
-
- ZERO_STRUCT(qt);
-
- /* access check */
- if (get_current_uid(conn) != sec_initial_uid()) {
- DEBUG(1,("get_user_quota: access_denied service [%s] user "
- "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
- conn->session_info->unix_info->unix_name));
- reply_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
- }
+ int i;
+ TALLOC_CTX *list_ctx = NULL;
- /*
- * Ensure minimum number of parameters sent.
- */
+ list_ctx = talloc_init("quota_sid_list");
- if (parameter_count < 4) {
- DEBUG(0,("TRANSACT_GET_USER_QUOTA: requires %d >= 4 bytes parameters\n",parameter_count));
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
+ if (list_ctx == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
}
- /* maybe we can check the quota_fnum */
- fsp = file_fsp(req, SVAL(params,0));
- if (!check_fsp_ntquota_handle(conn, req, fsp)) {
- DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
- reply_nterror(req, NT_STATUS_INVALID_HANDLE);
- return;
+ if (qt_handle->quota_list!=NULL) {
+ free_ntquota_list(&(qt_handle->quota_list));
}
+ for (i = 0; i < elems; i++) {
+ SMB_NTQUOTA_STRUCT qt;
+ SMB_NTQUOTA_LIST *list_item;
- /* the NULL pointer checking for fsp->fake_file_handle->pd
- * is done by CHECK_NTQUOTA_HANDLE_OK()
- */
- qt_handle = (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
-
- level = SVAL(params,2);
-
- /* unknown 12 bytes leading in params */
-
- switch (level) {
- case TRANSACT_GET_USER_QUOTA_LIST_CONTINUE:
- /* seems that we should continue with the enum here --metze */
-
- if (qt_handle->quota_list!=NULL &&
- qt_handle->tmp_list==NULL) {
-
- /* free the list */
- free_ntquota_list(&(qt_handle->quota_list));
-
- /* Realloc the size of parameters and data we will return */
- param_len = 4;
- params = nttrans_realloc(ppparams, param_len);
- if(params == NULL) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
+ if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp,
+ SMB_USER_QUOTA_TYPE,
+ &sids[i], &qt))) {
+ /* non fatal error, return empty item in result */
+ ZERO_STRUCT(qt);
+ continue;
+ }
- data_len = 0;
- SIVAL(params,0,data_len);
- break;
- }
+ list_item = talloc_zero(list_ctx, SMB_NTQUOTA_LIST);
+ if (list_item == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
+ }
- start_enum = False;
+ sid_to_uid(&sids[i], &list_item->uid);
+ list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT);
+ if (list_item->quotas == NULL) {
+ DBG_ERR("failed to allocate\n");
+ return NDR_ERR_ALLOC;
+ }
- FALL_THROUGH;
- case TRANSACT_GET_USER_QUOTA_LIST_START:
+ *list_item->quotas = qt;
+ list_item->mem_ctx = list_ctx;
+ DLIST_ADD(qt_handle->quota_list, list_item);
+ }
+ qt_handle->tmp_list = qt_handle->quota_list;
+ return NDR_ERR_SUCCESS;
+}
- if (qt_handle->quota_list==NULL &&
- qt_handle->tmp_list==NULL) {
- start_enum = True;
- }
+static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx,
+ uint32_t sidlistlength,
+ DATA_BLOB *sid_buf,
+ struct dom_sid **sids,
+ uint32_t *num)
+{
+ DATA_BLOB blob;
+ uint32_t i = 0;
+ enum ndr_err_code err;
+
+ struct sid_list_elem {
+ struct sid_list_elem *prev, *next;
+ struct dom_sid sid;
+ };
+
+ struct sid_list_elem *sid_list = NULL;
+ struct sid_list_elem *iter = NULL;
+ TALLOC_CTX *list_ctx = talloc_init("sid_list");
+ if (!list_ctx) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
- if (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0) {
- reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
- return;
- }
+ *num = 0;
+ *sids = NULL;
- /* Realloc the size of parameters and data we will return */
- param_len = 4;
- params = nttrans_realloc(ppparams, param_len);
- if(params == NULL) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
-
- /* we should not trust the value in max_data_count*/
- max_data_count = MIN(max_data_count,2048);
+ if (sidlistlength) {
+ uint32_t offset = 0;
+ struct ndr_pull *ndr_pull = NULL;
- pdata = nttrans_realloc(ppdata, max_data_count);/* should be max data count from client*/
- if(pdata == NULL) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
+ if (sidlistlength > sid_buf->length) {
+ DBG_ERR("sid_list_length 0x%x exceeds "
+ "available bytes %zx\n",
+ sidlistlength,
+ sid_buf->length);
+ err = NDR_ERR_OFFSET;
+ goto done;
+ }
+ while (true) {
+ struct file_get_quota_info info;
+ struct sid_list_elem *item = NULL;
+ uint32_t new_offset = 0;
+ blob.data = sid_buf->data + offset;
+ blob.length = sidlistlength - offset;
+ ndr_pull = ndr_pull_init_blob(&blob, list_ctx);
+ if (!ndr_pull) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
}
-
- entry = pdata;
-
- /* set params Size of returned Quota Data 4 bytes*/
- /* but set it later when we know it */
-
- /* for each entry push the data */
-
- if (start_enum) {
- qt_handle->tmp_list = qt_handle->quota_list;
+ err = ndr_pull_file_get_quota_info(ndr_pull,
+ NDR_SCALARS | NDR_BUFFERS, &info);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_ERR("Failed to pull file_get_quota_info "
+ "from sidlist buffer\n");
+ goto done;
}
-
- tmp_list = qt_handle->tmp_list;
-
- for (;((tmp_list!=NULL)&&((qt_len +40+SID_MAX_SIZE)<max_data_count));
- tmp_list=tmp_list->next,entry+=entry_len,qt_len+=entry_len) {
-
- sid_len = ndr_size_dom_sid(
- &tmp_list->quotas->sid, 0);
- entry_len = 40 + sid_len;
-
- /* nextoffset entry 4 bytes */
- SIVAL(entry,0,entry_len);
-
- /* then the len of the SID 4 bytes */
- SIVAL(entry,4,sid_len);
-
- /* unknown data 8 bytes uint64_t */
- SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-metze*/
-
- /* the used disk space 8 bytes uint64_t */
- SBIG_UINT(entry,16,tmp_list->quotas->usedspace);
-
- /* the soft quotas 8 bytes uint64_t */
- SBIG_UINT(entry,24,tmp_list->quotas->softlim);
-
- /* the hard quotas 8 bytes uint64_t */
- SBIG_UINT(entry,32,tmp_list->quotas->hardlim);
-
- /* and now the SID */
- sid_linearize((uint8_t *)(entry+40), sid_len,
- &tmp_list->quotas->sid);
+ item = talloc_zero(list_ctx, struct sid_list_elem);
+ if (!item) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
}
-
- qt_handle->tmp_list = tmp_list;
-
- /* overwrite the offset of the last entry */
- SIVAL(entry-entry_len,0,0);
-
- data_len = 4+qt_len;
- /* overwrite the params quota_data_len */
- SIVAL(params,0,data_len);
-
- break;
-
- case TRANSACT_GET_USER_QUOTA_FOR_SID:
-
- /* unknown 4 bytes IVAL(pdata,0) */
-
- if (data_count < 8) {
- DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %d bytes data\n",data_count,8));
- reply_nterror(req, NT_STATUS_INVALID_LEVEL);
- return;
+ item->sid = info.sid;
+ DLIST_ADD(sid_list, item);
+ i++;
+ if (i == UINT32_MAX) {
+ DBG_ERR("Integer overflow\n");
+ err = NDR_ERR_ARRAY_SIZE;
+ goto done;
}
+ new_offset = info.next_entry_offset;
- sid_len = IVAL(pdata,4);
- /* Ensure this is less than 1mb. */
- if (sid_len > (1024*1024)) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
+ /* if new_offset == 0 no more sid(s) to read. */
+ if (new_offset == 0) {
+ break;
}
- if (data_count < 8+sid_len) {
- DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: requires %d >= %lu bytes data\n",data_count,(unsigned long)(8+sid_len)));
- reply_nterror(req, NT_STATUS_INVALID_LEVEL);
- return;
+ /* Integer wrap? */
+ if ((offset + new_offset) < offset) {
+ DBG_ERR("Integer wrap while adding "
+ "new_offset 0x%x to current "
+ "buffer offset 0x%x\n",
+ new_offset, offset);
+ err = NDR_ERR_OFFSET;
+ goto done;
}
- data_len = 4+40+sid_len;
-
- if (max_data_count < data_len) {
- DEBUG(0,("TRANSACT_GET_USER_QUOTA_FOR_SID: max_data_count(%d) < data_len(%d)\n",
- max_data_count, data_len));
- param_len = 4;
- SIVAL(params,0,data_len);
- data_len = 0;
- nt_status = NT_STATUS_BUFFER_TOO_SMALL;
- break;
- }
+ offset += new_offset;
- if (!sid_parse((const uint8_t *)(pdata+8), sid_len,
- &sid)) {
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
+ /* check if new offset is outside buffer boundry. */
+ if (offset >= sidlistlength) {
+ DBG_ERR("bufsize 0x%x exceeded by "
+ "new offset 0x%x)\n",
+ sidlistlength,
+ offset);
+ err = NDR_ERR_OFFSET;
+ goto done;
}
+ }
+ *sids = talloc_zero_array(mem_ctx, struct dom_sid, i);
+ if (!sids) {
+ DBG_ERR("OOM\n");
+ err = NDR_ERR_ALLOC;
+ goto done;
+ }
- nt_status = vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE,
- &sid, &qt);
- if (!NT_STATUS_IS_OK(nt_status)) {
- reply_nterror(req, nt_status);
- return;
- }
+ *num = i;
- /* Realloc the size of parameters and data we will return */
- param_len = 4;
- params = nttrans_realloc(ppparams, param_len);
- if(params == NULL) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
+ for (iter = sid_list, i = 0; iter; iter = iter->next, i++) {
+ (*sids)[i] = iter->sid;
+ DBG_DEBUG("quota SID[%u] %s\n",
+ (unsigned int)i,
+ sid_string_dbg(&iter->sid));
+ }
+ }
+ err = NDR_ERR_SUCCESS;
+done:
+ TALLOC_FREE(list_ctx);
+ return err;
+}
- pdata = nttrans_realloc(ppdata, data_len);
- if(pdata == NULL) {
- reply_nterror(req, NT_STATUS_NO_MEMORY);
- return;
- }
+NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ bool restart_scan,
+ bool return_single,
+ uint32_t sid_list_length,
+ DATA_BLOB *sid_buf,
+ uint32_t max_data_count,
+ uint8_t **p_data,
+ uint32_t *p_data_size)
+{
+ NTSTATUS status;
+ SMB_NTQUOTA_HANDLE *qt_handle = NULL;
+ SMB_NTQUOTA_LIST *qt_list = NULL;
+ DATA_BLOB blob = data_blob_null;
+ enum ndr_err_code err;
- entry = pdata;
+ qt_handle =
+ (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
- /* set params Size of returned Quota Data 4 bytes*/
- SIVAL(params,0,data_len);
+ if (sid_list_length ) {
+ struct dom_sid *sids;
+ uint32_t elems = 0;
+ /*
+ * error check pulled offsets and lengths for wrap and
+ * exceeding available bytes.
+ */
+ if (sid_list_length > sid_buf->length) {
+ DBG_ERR("sid_list_length 0x%x exceeds "
+ "available bytes %zx\n",
+ sid_list_length,
+ sid_buf->length);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
- /* nextoffset entry 4 bytes */
- SIVAL(entry,0,0);
+ err = extract_sids_from_buf(mem_ctx, sid_list_length,
+ sid_buf, &sids, &elems);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ err = fill_qtlist_from_sids(mem_ctx,
+ fsp,
+ qt_handle,
+ sids,
+ elems);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else if (restart_scan) {
+ if (vfs_get_user_ntquota_list(fsp,
+ &(qt_handle->quota_list))!=0) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ } else {
+ if (qt_handle->quota_list!=NULL &&
+ qt_handle->tmp_list==NULL) {
+ free_ntquota_list(&(qt_handle->quota_list));
+ }
+ }
- /* then the len of the SID 4 bytes */
- SIVAL(entry,4,sid_len);
+ if (restart_scan !=0 ) {
+ qt_list = qt_handle->quota_list;
+ } else {
+ qt_list = qt_handle->tmp_list;
+ }
+ status = fill_quota_buffer(mem_ctx, qt_list,
+ return_single != 0,
+ &blob,
+ &qt_handle->tmp_list);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (blob.length > max_data_count) {
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
- /* unknown data 8 bytes uint64_t */
- SBIG_UINT(entry,8,(uint64_t)0); /* this is not 0 in windows...-mezte*/
+ *p_data = blob.data;
+ *p_data_size = blob.length;
+ return NT_STATUS_OK;
+}
- /* the used disk space 8 bytes uint64_t */
- SBIG_UINT(entry,16,qt.usedspace);
+/****************************************************************************
+ Reply to get user quota
+****************************************************************************/
- /* the soft quotas 8 bytes uint64_t */
- SBIG_UINT(entry,24,qt.softlim);
+static void call_nt_transact_get_user_quota(connection_struct *conn,
+ struct smb_request *req,
+ uint16_t **ppsetup,
+ uint32_t setup_count,
+ char **ppparams,
+ uint32_t parameter_count,
+ char **ppdata,
+ uint32_t data_count,
+ uint32_t max_data_count)
+{
+ NTSTATUS nt_status = NT_STATUS_OK;
+ char *params = *ppparams;
+ char *pdata = *ppdata;
+ int data_len = 0;
+ int param_len = 0;
+ files_struct *fsp = NULL;
+ DATA_BLOB blob = data_blob_null;
+ struct nttrans_query_quota_params info = {0};
+ enum ndr_err_code err;
+ TALLOC_CTX *tmp_ctx = NULL;
+ uint32_t resp_len = 0;
+ uint8_t *resp_data = 0;
- /* the hard quotas 8 bytes uint64_t */
- SBIG_UINT(entry,32,qt.hardlim);
+ tmp_ctx = talloc_init("ntquota_list");
+ if (!tmp_ctx) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
- /* and now the SID */
- sid_linearize((uint8_t *)(entry+40), sid_len, &sid);
+ /* access check */
+ if (get_current_uid(conn) != sec_initial_uid()) {
+ DEBUG(1,("get_user_quota: access_denied service [%s] user "
+ "[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
+ conn->session_info->unix_info->unix_name));
+ nt_status = NT_STATUS_ACCESS_DENIED;
+ goto error;
+ }
+
+ blob.data = (uint8_t*)params;
+ blob.length = parameter_count;
+
+ err = ndr_pull_struct_blob(&blob, tmp_ctx, &info,
+ (ndr_pull_flags_fn_t)ndr_pull_nttrans_query_quota_params);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DEBUG(0,("TRANSACT_GET_USER_QUOTA: failed to pull "
+ "query_quota_params."));
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+ DBG_DEBUG("info.return_single_entry = %u, info.restart_scan = %u, "
+ "info.sid_list_length = %u, info.start_sid_length = %u, "
+ "info.start_sid_offset = %u\n",
+ (unsigned int)info.return_single_entry,
+ (unsigned int)info.restart_scan,
+ (unsigned int)info.sid_list_length,
+ (unsigned int)info.start_sid_length,
+ (unsigned int)info.start_sid_offset);
+
+ /* set blob to point at data for further parsing */
+ blob.data = (uint8_t*)pdata;
+ blob.length = data_count;
+ /*
+ * Although MS-SMB ref is ambiguous here, a microsoft client will
+ * only ever send a start sid (as part of a list) with
+ * sid_list_length & start_sid_offset both set to the actual list
+ * length. Note: Only a single result is returned in this case
+ * In the case where either start_sid_offset or start_sid_length
+ * are set alone or if both set (but have different values) then
+ * it seems windows will return a number of entries from the start
+ * of the list of users with quotas set. This behaviour is undocumented
+ * and windows clients do not send messages of that type. As such we
+ * currently will reject these requests.
+ */
+ if (info.start_sid_length
+ || (info.sid_list_length != info.start_sid_offset)) {
+ DBG_ERR("TRANSACT_GET_USER_QUOTA: unsupported single or "
+ "compound sid format\n");
+ nt_status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
- break;
+ /* maybe we can check the quota_fnum */
+ fsp = file_fsp(req, info.fid);
+ if (!check_fsp_ntquota_handle(conn, req, fsp)) {
+ DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
+ nt_status = NT_STATUS_INVALID_HANDLE;
+ goto error;
+ }
+ nt_status = smbd_do_query_getinfo_quota(tmp_ctx,
+ fsp,
+ info.restart_scan,
+ info.return_single_entry,
+ info.sid_list_length,
+ &blob,
+ max_data_count,
+ &resp_data,
+ &resp_len);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MORE_ENTRIES)) {
+ goto error;
+ }
+ nt_status = NT_STATUS_OK;
+ }
- default:
- DEBUG(0, ("do_nt_transact_get_user_quota: %s: unknown "
- "level 0x%04hX\n",
- fsp_fnum_dbg(fsp), level));
- reply_nterror(req, NT_STATUS_INVALID_LEVEL);
- return;
- break;
+ param_len = 4;
+ params = nttrans_realloc(ppparams, param_len);
+ if(params == NULL) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto error;
}
+ data_len = resp_len;
+ SIVAL(params, 0, data_len);
+ pdata = nttrans_realloc(ppdata, data_len);
+ memcpy(pdata, resp_data, data_len);
+
+ TALLOC_FREE(tmp_ctx);
send_nt_replies(conn, req, nt_status, params, param_len,
pdata, data_len);
+ return;
+error:
+ TALLOC_FREE(tmp_ctx);
+ reply_nterror(req, nt_status);
}
/****************************************************************************
@@ -2592,10 +2699,13 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
char *pdata = *ppdata;
int data_len=0,param_len=0;
SMB_NTQUOTA_STRUCT qt;
- size_t sid_len;
+ struct file_quota_information info = {0};
+ enum ndr_err_code err;
struct dom_sid sid;
+ DATA_BLOB inblob;
files_struct *fsp = NULL;
-
+ TALLOC_CTX *ctx = NULL;
+ NTSTATUS status = NT_STATUS_OK;
ZERO_STRUCT(qt);
/* access check */
@@ -2603,8 +2713,8 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
DEBUG(1,("set_user_quota: access_denied service [%s] user "
"[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
conn->session_info->unix_info->unix_name));
- reply_nterror(req, NT_STATUS_ACCESS_DENIED);
- return;
+ status = NT_STATUS_ACCESS_DENIED;
+ goto error;
}
/*
@@ -2613,67 +2723,58 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
if (parameter_count < 2) {
DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= 2 bytes parameters\n",parameter_count));
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
}
/* maybe we can check the quota_fnum */
fsp = file_fsp(req, SVAL(params,0));
if (!check_fsp_ntquota_handle(conn, req, fsp)) {
DEBUG(3,("TRANSACT_GET_USER_QUOTA: no valid QUOTA HANDLE\n"));
- reply_nterror(req, NT_STATUS_INVALID_HANDLE);
- return;
+ status = NT_STATUS_INVALID_HANDLE;
+ goto error;
}
- if (data_count < 40) {
- DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %d bytes data\n",data_count,40));
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
+ ctx = talloc_init("set_user_quota");
+ if (!ctx) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
}
+ inblob.data = (uint8_t*)pdata;
+ inblob.length = data_count;
- /* offset to next quota record.
- * 4 bytes IVAL(pdata,0)
- * unused here...
- */
-
- /* sid len */
- sid_len = IVAL(pdata,4);
+ err = ndr_pull_struct_blob(
+ &inblob,
+ ctx,
+ &info,
+ (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
- if (data_count < 40+sid_len || (40+sid_len < sid_len)) {
- DEBUG(0,("TRANSACT_SET_USER_QUOTA: requires %d >= %lu bytes data\n",data_count,(unsigned long)40+sid_len));
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DEBUG(0,("TRANSACT_SET_USER_QUOTA: failed to pull "
+ "file_quota_information\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
}
+ qt.usedspace = info.quota_used;
- /* unknown 8 bytes in pdata
- * maybe its the change time in NTTIME
- */
-
- /* the used space 8 bytes (uint64_t)*/
- qt.usedspace = BVAL(pdata,16);
-
- /* the soft quotas 8 bytes (uint64_t)*/
- qt.softlim = BVAL(pdata,24);
-
- /* the hard quotas 8 bytes (uint64_t)*/
- qt.hardlim = BVAL(pdata,32);
+ qt.softlim = info.quota_threshold;
- if (!sid_parse((const uint8_t *)(pdata+40), sid_len, &sid)) {
- reply_nterror(req, NT_STATUS_INVALID_PARAMETER);
- return;
- }
+ qt.hardlim = info.quota_limit;
- DEBUGADD(8,("SID: %s\n", sid_string_dbg(&sid)));
-
- /* 44 unknown bytes left... */
+ sid = info.sid;
if (vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &sid, &qt)!=0) {
- reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
- return;
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto error;
}
send_nt_replies(conn, req, NT_STATUS_OK, params, param_len,
pdata, data_len);
+ TALLOC_FREE(ctx);
+ return;
+error:
+ TALLOC_FREE(ctx);
+ reply_nterror(req, status);
}
#endif /* HAVE_SYS_QUOTAS */
diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h
index 2980935e599..2a41d9d251d 100644
--- a/source3/smbd/proto.h
+++ b/source3/smbd/proto.h
@@ -640,6 +640,20 @@ NTSTATUS smbd_do_query_security_desc(connection_struct *conn,
uint32_t max_data_count,
uint8_t **ppmarshalled_sd,
size_t *psd_size);
+#ifdef HAVE_SYS_QUOTAS
+
+struct smb2_query_quota_info;
+
+NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
+ files_struct *fsp,
+ bool restart_scan,
+ bool return_single,
+ uint32_t sid_list_length,
+ DATA_BLOB *sidbuffer,
+ uint32_t max_data_count,
+ uint8_t **p_data,
+ uint32_t *p_data_size);
+#endif
void reply_nttrans(struct smb_request *req);
void reply_nttranss(struct smb_request *req);
--
2.18.0.345.g5c9ce644c3-goog
From bac966c94471a1e9761f9344b880f8368dae3bd9 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Fri, 26 May 2017 16:02:33 +0100
Subject: [PATCH 08/12] s3/smbd: smb2 server implementation for query get/set
info.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/smb2_getinfo.c | 86 +++++++++++++++++++++++++++++++++++--
source3/smbd/smb2_setinfo.c | 36 ++++++++++++++++
source3/wscript_build | 1 +
3 files changed, 120 insertions(+), 3 deletions(-)
diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c
index da82bf52378..05c57db902d 100644
--- a/source3/smbd/smb2_getinfo.c
+++ b/source3/smbd/smb2_getinfo.c
@@ -25,6 +25,8 @@
#include "../libcli/smb/smb_common.h"
#include "trans2.h"
#include "../lib/util/tevent_ntstatus.h"
+#include "librpc/gen_ndr/ndr_quota.h"
+#include "librpc/gen_ndr/ndr_security.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_SMB2
@@ -520,9 +522,87 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx,
break;
}
- case SMB2_GETINFO_QUOTA:
- tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
- return tevent_req_post(req, ev);
+ case SMB2_GETINFO_QUOTA: {
+ struct smb2_query_quota_info info;
+ enum ndr_err_code err;
+ uint8_t *data = NULL;
+ uint32_t data_size = 0;
+ struct ndr_pull *ndr_pull = NULL;
+ DATA_BLOB sid_buf = data_blob_null;
+ TALLOC_CTX *tmp_ctx = talloc_init("geninfo_quota");
+
+ if (!tmp_ctx) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ ndr_pull = ndr_pull_init_blob(&in_input_buffer, tmp_ctx);
+ if (!ndr_pull) {
+ TALLOC_FREE(tmp_ctx);
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return tevent_req_post(req, ev);
+ }
+
+ err = ndr_pull_smb2_query_quota_info(ndr_pull,
+ NDR_SCALARS | NDR_BUFFERS,
+ &info);
+
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ DBG_DEBUG("failed to pull smb2_query_quota_info\n");
+ TALLOC_FREE(tmp_ctx);
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return tevent_req_post(req, ev);
+ }
+
+ DBG_DEBUG("quota list returnsingle %u, restartscan %u, "
+ "sid_list_length %u, start_sid_length %u, "
+ "startsidoffset %u\n",
+ (unsigned int)info.return_single,
+ (unsigned int)info.restart_scan,
+ (unsigned int)info.sid_list_length,
+ (unsigned int)info.start_sid_length,
+ (unsigned int)info.start_sid_offset);
+
+ /* Currently we do not support the single start sid format */
+ if (info.start_sid_length != 0 || info.start_sid_offset != 0 ) {
+ DBG_INFO("illegal single sid query\n");
+ TALLOC_FREE(tmp_ctx);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ if (in_input_buffer.length < ndr_pull->offset) {
+ DBG_INFO("Invalid buffer length\n");
+ TALLOC_FREE(tmp_ctx);
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+
+ sid_buf.data = in_input_buffer.data + ndr_pull->offset;
+ sid_buf.length = in_input_buffer.length - ndr_pull->offset;
+
+ status = smbd_do_query_getinfo_quota(tmp_ctx,
+ fsp,
+ info.restart_scan,
+ info.return_single,
+ info.sid_list_length,
+ &sid_buf,
+ in_output_buffer_length,
+ &data,
+ &data_size);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+
+ state->out_output_buffer =
+ data_blob_talloc(state, data, data_size);
+ status = NT_STATUS_OK;
+ TALLOC_FREE(tmp_ctx);
+ break;
+ }
default:
DEBUG(10,("smbd_smb2_getinfo_send: "
diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c
index 1f07b7e1190..7ed24231a83 100644
--- a/source3/smbd/smb2_setinfo.c
+++ b/source3/smbd/smb2_setinfo.c
@@ -28,6 +28,7 @@
#include "../librpc/gen_ndr/open_files.h"
#include "source3/lib/dbwrap/dbwrap_watch.h"
#include "messages.h"
+#include "librpc/gen_ndr/ndr_quota.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_SMB2
@@ -553,6 +554,41 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx,
break;
}
+ case 0x04:/* SMB2_SETINFO_QUOTA */
+ {
+ struct file_quota_information info = {0};
+ SMB_NTQUOTA_STRUCT qt = {0};
+ enum ndr_err_code err;
+ int ret;
+
+ if (!fsp->fake_file_handle) {
+ tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+ return tevent_req_post(req, ev);
+ }
+ err = ndr_pull_struct_blob(
+ &in_input_buffer, state, &info,
+ (ndr_pull_flags_fn_t)ndr_pull_file_quota_information);
+ if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
+ tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
+ return tevent_req_post(req, ev);
+ }
+
+ qt.usedspace = info.quota_used;
+
+ qt.softlim = info.quota_threshold;
+
+ qt.hardlim = info.quota_limit;
+
+ qt.sid = info.sid;
+ ret = vfs_set_ntquota(fsp, SMB_USER_QUOTA_TYPE, &qt.sid, &qt);
+ if (ret !=0 ) {
+ status = map_nt_error_from_unix(errno);
+ tevent_req_nterror(req, status);
+ return tevent_req_post(req, ev);
+ }
+ status = NT_STATUS_OK;
+ break;
+ }
default:
tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
return tevent_req_post(req, ev);
diff --git a/source3/wscript_build b/source3/wscript_build
index 7e38b79c3e2..6e34bfaecf0 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -740,6 +740,7 @@ bld.SAMBA3_LIBRARY('smbd_base',
NDR_IOCTL
notifyd
vfs_acl_common
+ NDR_QUOTA
''' +
bld.env['dmapi_lib'] +
bld.env['legacy_quota_libs'] +
--
2.18.0.345.g5c9ce644c3-goog
From 08a18a459bc7051fc471545eec21e7c53dfabb8d Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 8 Mar 2017 14:27:27 +0000
Subject: [PATCH 09/12] s3/script/test: modify existing smbcquota test to use
SMB2 in addition to SMB1.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/script/tests/test_dfree_quota.sh | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/source3/script/tests/test_dfree_quota.sh b/source3/script/tests/test_dfree_quota.sh
index abd82b46751..444a6684942 100755
--- a/source3/script/tests/test_dfree_quota.sh
+++ b/source3/script/tests/test_dfree_quota.sh
@@ -164,13 +164,21 @@ test_smbcquotas() {
conf="$2"
user="$3"
expected="$4"
+ proto="$5"
shift
shift
shift
+ shift
shift
subunit_start_test "$name"
setup_conf "$conf" "."
- output=$($VALGRIND $smbcquotas //$SERVER/dfq $@ 2>/dev/null | tr '\\' '/')
+ if [ "$proto" = "smb2" ]; then
+ mproto="-m SMB2"
+ else
+ mproto="-m SMB1"
+ fi
+
+ output=$($VALGRIND $smbcquotas $mproto //$SERVER/dfq $@ 2>/dev/null | tr '\\' '/')
status=$?
if [ "$status" = "0" ]; then
received=$(echo "$output" | awk "/$SERVER\\/$user/ {printf \"%s%s%s\", \$3, \$4, \$5}")
@@ -191,7 +199,9 @@ test_smbclient_dfree "Test dfree subdir SMB3 no quota" dfq "subdir1" "conf1 . co
test_smbclient_dfree "Test dfree subdir NT1 no quota" dfq "subdir1" "conf1 . conf2 subdir1" "10 1024. 5" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=NT1 || failed=`expr $failed + 1`
test_smbclient_dfree "Test large disk" dfq "." "conf3 ." "1125899906842624 1024. 3000" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB3 || failed=`expr $failed + 1`
#basic quota test (SMB1 only)
-test_smbcquotas "Test user quota" confq1 $USERNAME "40960/4096000/3072000" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=NT1 || failed=`expr $failed + 1`
+test_smbcquotas "Test user quota" confq1 $USERNAME "40960/4096000/3072000" "smb1" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=NT1 || failed=`expr $failed + 1`
+#basic quota test (SMB2 only)
+test_smbcquotas "Test user quota" confq1 $USERNAME "40960/4096000/3072000" "smb2" -U$USERNAME%$PASSWORD --option=clientmaxprotocol=SMB2 || failed=`expr $failed + 1`
# Test dfree cache through queries in two different directories
test_smbclient_dfree_2 "Test dfree cache" dfq_cache "." "subdir1" \
--
2.18.0.345.g5c9ce644c3-goog
From 3fe042e7d0d0ad57303b37b5cd1bf46f9f4fb1ff Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Tue, 21 Mar 2017 08:29:59 +0000
Subject: [PATCH 10/12] s3/script/tests: Add simple (smb1 & smb2) get/set/list
tests for smbcquotas
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
selftest/target/Samba3.pm | 9 +
source3/script/tests/getset_quota.py | 154 ++++++++++++++++
source3/script/tests/test_smbcquota.py | 244 +++++++++++++++++++++++++
source3/script/tests/test_smbcquota.sh | 46 +++++
source3/selftest/tests.py | 1 +
5 files changed, 454 insertions(+)
create mode 100755 source3/script/tests/getset_quota.py
create mode 100755 source3/script/tests/test_smbcquota.py
create mode 100755 source3/script/tests/test_smbcquota.sh
diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm
index 5b8cf9ea6d8..447c1e8e3a7 100755
--- a/selftest/target/Samba3.pm
+++ b/selftest/target/Samba3.pm
@@ -885,6 +885,9 @@ sub setup_fileserver
push(@dirs, "$dfree_share_dir/subdir2");
push(@dirs, "$dfree_share_dir/subdir3");
+ my $quotadir_dir="$share_dir/quota";
+ push(@dirs, $quotadir_dir);
+
my $valid_users_sharedir="$share_dir/valid_users";
push(@dirs,$valid_users_sharedir);
@@ -911,6 +914,8 @@ sub setup_fileserver
usershare allow guests = yes
usershare prefix allow list = $usershare_sharedir
+ get quota command = $prefix_abs/getset_quota.py
+ set quota command = $prefix_abs/getset_quota.py
[lowercase]
path = $lower_case_share_dir
comment = smb username is [%U]
@@ -2170,6 +2175,10 @@ sub provision($$$$$$$$$)
vfs objects = acl_xattr fake_acls xattr_tdb fake_dfq
inherit owner = yes
include = $dfqconffile
+[quotadir]
+ path = $shrdir/quota
+ admin users = $unix_name
+
[acl_xattr_ign_sysacl_posix]
copy = tmp
acl_xattr:ignore system acls = yes
diff --git a/source3/script/tests/getset_quota.py b/source3/script/tests/getset_quota.py
new file mode 100755
index 00000000000..0254aa5a3b3
--- /dev/null
+++ b/source3/script/tests/getset_quota.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+# Unix SMB/CIFS implementation.
+# Tests for smbcquotas
+# Copyright (C) Noel Power 2017
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import traceback
+import logging
+import os
+
+USER_QUOTAS = 1
+USER_DEFAULT_QUOTAS = 2
+GROUP_QUOTAS = 3
+GROUP_DEFAULT_QUOTAS = 4
+
+#Quota model
+
+class Quota:
+ def __init__(self):
+ self.flags = 0
+ self.quotatype = USER_DEFAULT_QUOTAS
+ self.uid = 0
+ self.usedblocks = 0
+ self.softlimit = 0
+ self.hardlimit = 0
+ self.hardlimit = 0
+ self.usedinodes = 0
+ self.slimitinodes = 0
+ self.hlimitinodes = 0
+
+def quota_to_str(item):
+ result = str(item.flags) + " " + str(item.usedblocks) + " " + str(item.softlimit) + " " + str(item.hardlimit) + " " + str(item.usedinodes) + " " + str(item.slimitinodes) + " " + str(item.hlimitinodes)
+ return result
+
+def quota_to_db_str(item):
+ result = item.uid + " " + str(item.usedblocks) + " " + str(item.softlimit) + " " + str(item.hardlimit) + " " + str(item.usedinodes) + " " + str(item.slimitinodes) + " " + str(item.hlimitinodes)
+ return result
+
+def load_quotas(input_file):
+ fileContents = open(input_file,"r")
+ lineno = 0
+ quotas = []
+ for line in fileContents:
+ if line.strip().startswith("#"):
+ continue
+ content = line.strip().split()
+ quota = Quota()
+ if len(content) < 7:
+ logging.debug("ignoring line %d, doesn't have enough fields\n"%lineno)
+ else:
+ quota.flags = 2
+ quota.uid = content[0]
+ quota.usedblocks = content[1]
+ quota.softlimit = content[2]
+ quota.hardlimit = content[3]
+ quota.usedinodes = content[4]
+ quota.slimitinodes = content[5]
+ quota.hlimitinodes = content[6]
+ quotas.append(quota)
+
+ fileContents.close()
+ return quotas
+
+def set_quotas(quota_list, output_file):
+ filecontents = open(output_file,"w+")
+ if filecontents == None:
+ return False;
+ lines = ""
+ for quota in quota_list:
+ next_line = quota_to_db_str(quota)
+ if next_line:
+ lines = lines + next_line + "\n"
+ filecontents.write(lines)
+ filecontents.close()
+ return True
+
+def get_quotas(uid, quota_list):
+ logging.debug("in get_quotas\n")
+ for quota in quota_list:
+ if quota.uid == uid:
+ return quota
+ return None
+
+def main():
+ logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
+ logging.debug("system args passed are %s\n"% str(sys.argv))
+ quota_file_dir = os.path.dirname(sys.argv[0]);
+ quota_file_db = os.path.join(quota_file_dir,"quotas.db")
+ logging.debug("quota db is located %s\n", quota_file_db)
+ quota_list = load_quotas(quota_file_db)
+ logging.debug("quotas loaded have %s entries\n", len(quota_list))
+ result = None
+ if len(sys.argv) == 4:
+ # Get Quota
+ directory = sys.argv[1]
+ if sys.argv[2] == "1":
+ query_type = USER_QUOTAS
+ elif sys.argv[2] == "2":
+ query_type = USER_DEFAULT_QUOTAS
+ elif sys.argv[2] == "3":
+ query_type = GROUP_QUOTAS
+ elif sys.argv[2] == "4":
+ query_type = GROUP_DEFAULT_QUOTAS
+ uid = sys.argv[3]
+ quota = get_quotas(uid, quota_list)
+ if quota is None:
+ logging.debug("no result for uid %s"%uid)
+ else:
+ result = quota_to_str(quota)
+ logging.debug("got result for uid %s\n"%uid);
+ if result is None:
+ result = "0 0 0 0 0 0 0"
+ logging.debug("for uid %s returning quotas %s\n"%(uid,result))
+ print("%s"%result)
+ elif len(sys.argv) > 8:
+ # Set Quota
+ quota = Quota()
+ directory = sys.argv[1]
+ quota.query_type = sys.argv[2]
+ quota.uid = sys.argv[3]
+ quota.flags = sys.argv[4]
+ quota.softlimit = sys.argv[5]
+ quota.hardlimit = sys.argv[6]
+ quota.slimitinodes = sys.argv[7]
+ quota.hlimitinodes = sys.argv[8]
+ found = get_quotas(quota.uid, quota_list)
+ if found:
+ found.query_type = quota.query_type
+ found.uid = quota.uid
+ found.flags = quota.flags
+ found.softlimit = quota.softlimit
+ found.hardlimit = quota.hardlimit
+ found.slimitinodes = quota.slimitinodes
+ found.hlimitinodes = quota.hlimitinodes
+ else:
+ quota_list.append(quota)
+ if set_quotas(quota_list,quota_file_db):
+ print ("%s\n"%quota_to_str(quota_list[-1]))
+ return
+if __name__ == '__main__':
+ main()
diff --git a/source3/script/tests/test_smbcquota.py b/source3/script/tests/test_smbcquota.py
new file mode 100755
index 00000000000..52061f2989f
--- /dev/null
+++ b/source3/script/tests/test_smbcquota.py
@@ -0,0 +1,244 @@
+#!/usr/bin/env python3
+# Unix SMB/CIFS implementation.
+# Tests for smbcquotas
+# Copyright (C) Noel Power 2017
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os, subprocess, sys
+import traceback
+import logging
+import shutil
+
+USER_QUOTAS = 1
+USER_DEFAULT_QUOTAS = 2
+GROUP_QUOTAS = 3
+GROUP_DEFAULT_QUOTAS = 4
+BLOCK_SIZE = 1024
+DEFAULT_SOFTLIM = 2
+DEFAULT_HARDLIM = 4
+
+class test_env:
+ def __init__(self):
+ self.server = None
+ self.domain = None
+ self.username = None
+ self.password = None
+ self.envdir = None
+ self.quota_db = None
+ self.smbcquotas = None
+ self.users = []
+
+class user_info:
+ def __init__(self):
+ self.uid = 0
+ self.username = ""
+ self.softlim = 0
+ self.hardlim = 0
+
+class Quota:
+ def __init__(self):
+ self.flags = 0
+ self.quotatype = USER_DEFAULT_QUOTAS
+ self.uid = 0
+ self.usedblocks = 0
+ self.softlimit = 0
+ self.hardlimit = 0
+ self.hardlimit = 0
+ self.usedinodes = 0
+ self.slimitinodes = 0
+ self.hlimitinodes = 0
+
+def init_quota_db(users, output_file):
+ filecontents = open(output_file,"w+")
+ lines = ""
+ default_values = "0 " + str(DEFAULT_SOFTLIM) + " " + str(DEFAULT_HARDLIM) + " 0 0 0"
+ for user in users:
+ lines = lines + user.uid + " " + default_values + "\n"
+ filecontents.write(lines)
+ filecontents.close()
+
+def load_quotas(input_file):
+ fileContents = open(input_file,"r")
+ lineno = 0
+ quotas = []
+ for line in fileContents:
+ if line.strip().startswith("#"):
+ continue
+ content = line.strip().split()
+ quota = Quota()
+ if len(content) < 7:
+ logging.debug("ignoring line %d, doesn't have enough fields\n"%lineno)
+ else:
+ quota.flags = 2
+ quota.uid = content[0]
+ quota.usedblocks = content[1]
+ quota.softlimit = content[2]
+ quota.hardlimit = content[3]
+ quota.usedinodes = content[4]
+ quota.slimitinodes = content[5]
+ quota.hlimitinodes = content[6]
+ quotas.append(quota)
+
+ fileContents.close()
+ return quotas
+
+def get_quotas(uid, quota_list):
+ for quota in quota_list:
+ if quota.uid == uid:
+ return quota
+ return None
+
+def get_users():
+ output = subprocess.Popen(['getent', 'passwd'],
+ stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n')
+ users = []
+ for line in output:
+ info = line.split(':')
+ if len(info) > 3 and info[0]:
+ user = user_info()
+ user.username = info[0]
+ user.uid = info[2]
+ logging.debug("Adding user ->%s<-\n"%user.username)
+ users.append(user)
+ return users
+
+
+
+def smbcquota_output_to_userinfo(output):
+ infos = []
+ for line in output:
+ if len(line) > 1:
+ username = line.strip(':').split()[0]
+ quota_info = line.split(':')[1].split('/')
+ if len(quota_info) > 2:
+ info = user_info()
+ info.username = username.strip()
+ info.softlim = int(quota_info[1].strip()) / BLOCK_SIZE
+ info.hardlim = int(quota_info[2].strip()) / BLOCK_SIZE
+ infos.append(info)
+ return infos
+
+def check_quota_limits(infos, softlim, hardlim):
+ if len(infos) < 1:
+ logging.debug("no users info to check :-(\n")
+ return False
+ for info in infos:
+ if int(info.softlim) != softlim:
+ logging.debug("expected softlimit %s got ->%s<-\n"%(softlim, info.softlim))
+ return False
+ if int(info.hardlim) != hardlim:
+ logging.debug("expected hardlimit limit %s got %s\n"%(hardlim,info.hardlim))
+ return False
+ return True
+
+class test_base:
+ def __init__(self, env):
+ self.env = env
+ def run(self, protocol):
+ pass
+
+class listtest(test_base):
+ def run(self, protocol):
+ init_quota_db(self.env.users, self.env.quota_db)
+ quotas = load_quotas(self.env.quota_db)
+ args = [self.env.smbcquotas];
+ remaining_args = ['-U' + self.env.username + "%" + self.env.password, '-L', '//' + self.env.server + '/quotadir']
+ if protocol == 'smb2':
+ args.append('-m smb2')
+ args.extend(remaining_args)
+ output = subprocess.Popen([self.env.smbcquotas, '-U' + self.env.username + "%" + self.env.password, '-L', '//' + self.env.server + '/quotadir'], stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n')
+ infos = smbcquota_output_to_userinfo(output)
+ return check_quota_limits(infos, DEFAULT_SOFTLIM, DEFAULT_HARDLIM)
+def get_uid(name, users):
+ for user in users:
+ if user.username == name:
+ return user.uid
+ return None
+
+class gettest(test_base):
+ def run(self, protocol):
+ init_quota_db(self.env.users, self.env.quota_db)
+ quotas = load_quotas(self.env.quota_db)
+ uid = get_uid(self.env.username, self.env.users)
+ output = subprocess.Popen([self.env.smbcquotas, '-U' + self.env.username + "%" + self.env.password, '-u' + self.env.username, '//' + self.env.server + '/quotadir'], stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n')
+ user_infos = smbcquota_output_to_userinfo(output)
+ db_user_info = get_quotas(uid, quotas)
+ # double check, we compare the results from the db file
+ # the quota script the server uses compared to what
+ # smbcquota is telling us
+ return check_quota_limits(user_infos, int(db_user_info.softlimit), int(db_user_info.hardlimit))
+
+class settest(test_base):
+ def run(self, protocol):
+ init_quota_db(self.env.users, self.env.quota_db)
+ quotas = load_quotas(self.env.quota_db)
+ uid = get_uid(self.env.username, self.env.users)
+ old_db_user_info = get_quotas(uid, quotas)
+
+ #increase limits by 2 blocks
+ new_soft_limit = (int(old_db_user_info.softlimit) + 2) * BLOCK_SIZE
+ new_hard_limit = (int(old_db_user_info.hardlimit) + 2) * BLOCK_SIZE
+
+ new_limits = "UQLIM:%s:%d/%d"%(self.env.username, new_soft_limit, new_hard_limit)
+ logging.debug("setting new limits %s"%new_limits)
+
+ output = subprocess.Popen([self.env.smbcquotas, '-U' + self.env.username + "%" + self.env.password, '//' + self.env.server + '/quotadir', '-S', new_limits], stdout=subprocess.PIPE).communicate()[0].decode("utf-8").split('\n')
+ logging.debug("output from smbcquota is %s"%output)
+ user_infos = smbcquota_output_to_userinfo(output)
+ return check_quota_limits(user_infos, new_soft_limit / BLOCK_SIZE, new_hard_limit / BLOCK_SIZE)
+
+# map of tests
+subtest_descriptions = {
+ "list test" : listtest,
+ "get test" : gettest,
+ "set test" : settest
+}
+
+def main():
+ logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG)
+
+ logging.debug("got args %s\n"%str(sys.argv))
+
+ if len(sys.argv) < 7:
+ logging.debug ("Usage: test_smbcquota.py server domain username password envdir smbcquotas\n")
+ sys.exit(1)
+ env = test_env()
+ env.server = sys.argv[1]
+ env.domain = sys.argv[2]
+ env.username = sys.argv[3]
+ env.password = sys.argv[4]
+ env.envdir = sys.argv[5]
+ env.smbcquotas = sys.argv[6]
+ quota_script = os.path.join(os.path.dirname(sys.argv[0]),
+ "getset_quota.py")
+ #copy the quota script to the evironment
+ shutil.copy2(quota_script, env.envdir)
+
+ env.quota_db = os.path.join(env.envdir, "quotas.db")
+ env.users = get_users()
+ for protocol in ['smb1', 'smb2']:
+ for key in subtest_descriptions.keys():
+ test = subtest_descriptions[key](env)
+ logging.debug("running subtest '%s' using protocol '%s'\n"%(key,protocol))
+ result = test.run(protocol)
+ if result == False:
+ logging.debug("subtest '%s' for '%s' failed\n"%(key,protocol))
+ sys.exit(1)
+ else:
+ logging.debug("subtest '%s' for '%s' passed\n"%(key,protocol))
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
diff --git a/source3/script/tests/test_smbcquota.sh b/source3/script/tests/test_smbcquota.sh
new file mode 100755
index 00000000000..a61c2fe6d0b
--- /dev/null
+++ b/source3/script/tests/test_smbcquota.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+# Unix SMB/CIFS implementation.
+# Tests for smbcquotas
+# Copyright (C) Noel Power 2017
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+#
+# Blackbox test wrapper for smbcquota
+#
+if [ $# -lt 6 ]; then
+cat <<EOF
+Usage: test_smbcquota.sh SERVER DOMAIN USERNAME PASSWORD LOCAL_PATH SMBCQUOTAS
+EOF
+exit 1;
+fi
+
+SERVER=$1
+DOMAIN=$2
+USERNAME=$3
+PASSWORD=$4
+ENVDIR=`dirname $5`
+SMBCQUOTAS="$VALGRIND $6"
+shift 6
+
+TEST_SMBCQUOTAS=`dirname $0`/test_smbcquota.py
+
+incdir=`dirname $0`/../../../testprogs/blackbox
+. $incdir/subunit.sh
+
+
+testit "smbcquotas" ${TEST_SMBCQUOTAS} ${SERVER} ${DOMAIN} ${USERNAME} ${PASSWORD} ${ENVDIR} ${SMBCQUOTAS} || failed=`expr $failed + 1`
+
+testok $0 $failed
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index b2b2ff26077..d3c54870377 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -284,6 +284,7 @@ for env in ["fileserver"]:
plantestsuite("samba3.blackbox.preserve_case (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_preserve_case.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
plantestsuite("samba3.blackbox.dfree_command (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_dfree_command.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
plantestsuite("samba3.blackbox.dfree_quota (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_dfree_quota.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbclient3, smbcquotas, smbcacls])
+ plantestsuite("samba3.blackbox.smbcquotas (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_smbcquota.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', smbcquotas])
plantestsuite("samba3.blackbox.valid_users (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_valid_users.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', smbclient3])
plantestsuite("samba3.blackbox.offline (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_offline.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/offline', smbclient3])
plantestsuite("samba3.blackbox.shadow_copy2 NT1 (%s)" % env, env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'NT1'])
--
2.18.0.345.g5c9ce644c3-goog
From f507983e3b932c46462e265d52cdfe3450b62fb4 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Wed, 22 Mar 2017 20:06:13 +0000
Subject: [PATCH 11/12] s3/smbd: allow set quota for non root user (when built
with --enable-selftest)
Currently it appears you need to be root to set quotas, for test purposes
this requirement needs to be relaxed.
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/smbd/nttrans.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c
index 391caa48199..db4565d208e 100644
--- a/source3/smbd/nttrans.c
+++ b/source3/smbd/nttrans.c
@@ -2709,7 +2709,7 @@ static void call_nt_transact_set_user_quota(connection_struct *conn,
ZERO_STRUCT(qt);
/* access check */
- if (get_current_uid(conn) != 0) {
+ if (get_current_uid(conn) != sec_initial_uid()) {
DEBUG(1,("set_user_quota: access_denied service [%s] user "
"[%s]\n", lp_servicename(talloc_tos(), SNUM(conn)),
conn->session_info->unix_info->unix_name));
--
2.18.0.345.g5c9ce644c3-goog
From be441176bdcb2857765fc8c71156d54905655204 Mon Sep 17 00:00:00 2001
From: Noel Power <noel.power at suse.com>
Date: Thu, 28 Jun 2018 16:04:24 +0100
Subject: [PATCH 12/12] s3/utils: fix regression where specifying
-Unetbios/root works
Usually you need to be root on a linux server to modify quotas. Even
with a linux server joined to a windows AD you could always log in as
local root with smbcquotas. However in recent builds this has changed.
This patch fixes this
Signed-off-by: Noel Power <noel.power at suse.com>
Reviewed-by: Jeremy Allison <jra at samba.org>
---
source3/utils/smbcquotas.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
index 031862f52a1..798b8b6f177 100644
--- a/source3/utils/smbcquotas.c
+++ b/source3/utils/smbcquotas.c
@@ -533,7 +533,8 @@ static struct cli_state *connect_one(const char *share)
share, "?????",
get_cmdline_auth_info_username(
popt_get_cmdline_auth_info()),
- lp_workgroup(),
+ get_cmdline_auth_info_domain(
+ popt_get_cmdline_auth_info()),
get_cmdline_auth_info_password(
popt_get_cmdline_auth_info()),
flags,
--
2.18.0.345.g5c9ce644c3-goog
More information about the samba-technical
mailing list