From a72a0924febc213e3f6e70f1099e334b88525731 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 22 Mar 2017 14:53:22 +0000 Subject: [PATCH 01/18] s3/lib: Fix misleading typo in debug message BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit acacc78678b5a8442a49ac8f5730c5ebb6cb825c) --- 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.13.7 From f35e784f0d087dc2ef3a6c41d6ae786b649baf5b Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 28 Feb 2017 15:04:16 +0000 Subject: [PATCH 02/18] 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. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit f4a456a969082d47c3c0d7ff5d881a6e8b883830) --- 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 8c6c1851d2f..8313970ef15 100644 --- a/source3/libsmb/cli_smb2_fnum.c +++ b/source3/libsmb/cli_smb2_fnum.c @@ -2982,6 +2982,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.13.7 From 414630555e3e9597d874c666a371bf9128aa766c Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 28 Feb 2017 11:36:47 +0000 Subject: [PATCH 03/18] 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. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit d10a32cf881049e0e949dd5236380b0758ab02da) --- 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 d028dd035f2..21e0a67d12f 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -3458,7 +3458,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.13.7 From 4d7965da28a2e2bf022042a9643500cce20d0917 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 2 Mar 2017 09:20:24 +0000 Subject: [PATCH 04/18] librpc/idl Add some query [getset]info quota related structures BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 964626ec57799216f24b54b429071acd75a3e862) --- 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 899708cd5d6..e03bb712f30 100644 --- a/librpc/idl/wscript_build +++ b/librpc/idl/wscript_build @@ -37,6 +37,7 @@ bld.SAMBA_PIDL_LIST('PIDL', printcap.idl fsrvp_state.idl cab.idl + quota.idl ''', options='--header --ndr-parser', output_dir='../gen_ndr') diff --git a/librpc/wscript_build b/librpc/wscript_build index c4b647c242d..0711b8b8aee 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.13.7 From 7af5d6d2b032f9ba480b503fb7fc9b5b2b57d1e9 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 26 May 2017 15:01:17 +0100 Subject: [PATCH 05/18] s3/libsmb: adjust smb1 cli code to use idl structs and ndr push/pull funcs. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit f1e7ffd833d2ccb4e87cccef88f336e4fe074c13) --- source3/libsmb/cliquota.c | 397 +++++++++++++++++++++++++--------------------- source3/libsmb/proto.h | 6 + source3/wscript_build | 1 + 3 files changed, 224 insertions(+), 180 deletions(-) diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c index e22ccdd1931..7e8565b44b1 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; - - ZERO_STRUCT(qt); - - if (!rdata||!offset||!pqt) { - smb_panic("parse_quota_record: called with NULL POINTER!"); - } - - if (rdata_count < 40) { - return False; - } + struct file_quota_information info = {0}; + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB blob; + enum ndr_err_code err; + bool result = false; - /* offset to next quota record. - * 4 bytes IVAL(rdata,0) - * unused here... - */ - *offset = IVAL(rdata,0); + 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); - /* sid len */ - sid_len = IVAL(rdata,4); - if (40 + sid_len < 40) { - return false; + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + goto out; } - if (rdata_count < 40+sid_len) { - return False; - } + *offset = info.next_entry_offset; - if (*offset != 0 && *offset < 40 + sid_len) { - return false; - } + ZERO_STRUCTP(pqt); + pqt->usedspace = info.quota_used; - /* 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, @@ -217,111 +196,12 @@ NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, DATA_BLOB *outbuf, SMB_NTQUOTA_LIST **end_ptr) { - uint32_t qt_len = 0; - uint8_t *entry; - uint32_t entry_len; - int sid_len; - SMB_NTQUOTA_LIST *qtl; - DATA_BLOB qbuf = data_blob_null; - NTSTATUS status = NT_STATUS_UNSUCCESSFUL; - - 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, + maxlen, + outbuf, + end_ptr); } NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx, @@ -367,43 +247,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 +321,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 +329,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; } @@ -442,7 +354,15 @@ cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl) status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL); if (!NT_STATUS_IS_OK(status)) { - goto cleanup; + /* + * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow + * this status. + */ + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + status = NT_STATUS_OK; + } else { + goto cleanup; + } } SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA); @@ -477,31 +397,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 +455,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 +583,108 @@ 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, + uint32_t max_data, + 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; + uint32_t dsize = sizeof(info.next_entry_offset) + + sizeof(info.sid_length) + + sizeof(info.change_time) + + sizeof(info.quota_used) + + sizeof(info.quota_threshold) + + sizeof(info.quota_limit); + + + info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0); + + if (max_data) { + uint32_t curr_pos_no_padding = qndr->offset - padding; + uint32_t payload = dsize + info.sid_length; + uint32_t new_pos = (curr_pos_no_padding + payload); + if (new_pos < curr_pos_no_padding) { + /* Detect unlikely integer wrap */ + DBG_ERR("Integer wrap while adjusting pos " + "0x%x by offset 0x%x\n", + curr_pos_no_padding, payload); + return NT_STATUS_INTERNAL_ERROR; + } + if (new_pos > max_data) { + DBG_WARNING("Max data will be exceeded " + "writing next query info. " + "cur_pos 0x%x, sid_length 0x%x, " + "dsize 0x%x, max_data 0x%x\n", + curr_pos_no_padding, + info.sid_length, + dsize, + max_data); + break; + } + } + + start_offset = qndr->offset; + 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. + */ + if (blob->length >= sizeof(uint32_t)) { + SIVAL(qndr->data, start_offset, 0); + } + return NT_STATUS_OK; +} diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h index 11fa409c821..a83e5722eb7 100644 --- a/source3/libsmb/proto.h +++ b/source3/libsmb/proto.h @@ -967,6 +967,12 @@ 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, + uint32_t max_data, + 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 cc511c376d8..e82799a3b6a 100644 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -480,6 +480,7 @@ bld.SAMBA3_LIBRARY('libsmb', LIBTSOCKET KRBCLIENT NDR_IOCTL + NDR_QUOTA cli_smb_common util_cmdline tevent -- 2.13.7 From 1326ee01d9e2bfc4f0b166d08d1379eed66edfee Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 26 May 2017 15:50:18 +0100 Subject: [PATCH 06/18] s3/libsmb: adjust smb2 code for new idl structs & generated ndr push/pull funcs. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 34208d31bfd7150f6330315e3897b02a50fb8ea4) --- 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 8313970ef15..8df7da77b0f 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; @@ -2845,12 +2846,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)) { /* @@ -2872,27 +2877,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 */ @@ -2937,7 +2967,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)) { /* @@ -2957,20 +2988,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.13.7 From f499bb912127cee9ab553b6dfe5281c0b6ed416b Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 26 May 2017 16:01:53 +0100 Subject: [PATCH 07/18] s3/smbd: adjust smb1 server to use idl structs and generated ndr push/pull funcs BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit aa7fb239243db4d9b49a31d628aa9e705ebe49f8) --- source3/libsmb/cliquota.c | 19 +- source3/smbd/nttrans.c | 659 +++++++++++++++++++++++++++------------------- source3/smbd/proto.h | 14 + 3 files changed, 402 insertions(+), 290 deletions(-) diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c index 7e8565b44b1..8efd2bbe38a 100644 --- a/source3/libsmb/cliquota.c +++ b/source3/libsmb/cliquota.c @@ -254,10 +254,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!"); @@ -292,16 +292,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; @@ -312,7 +307,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 7b7f2056099..6967e4e1b37 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,284 +2295,391 @@ 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; + } - 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)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, + max_data_count, + &blob, + &qt_handle->tmp_list); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (blob.length > max_data_count) { + return NT_STATUS_BUFFER_TOO_SMALL; + } - /* 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); } /**************************************************************************** @@ -2591,10 +2700,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 */ @@ -2602,8 +2714,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; } /* @@ -2612,67 +2724,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 d814a30711f..bffd12ffce8 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -633,6 +633,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.13.7 From 03286b3d4ed90c15f3e1f86fd7fb86d0c31fe7a6 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Fri, 26 May 2017 16:02:33 +0100 Subject: [PATCH 08/18] s3/smbd: smb2 server implementation for query get/set info. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 87bf24407ab39941d7a827c982bbc13cd09e9321) --- 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 694e9f83b75..2b5bb0772fd 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 ceab15ac8fd..266991760d6 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 @@ -570,6 +571,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 e82799a3b6a..16009d1123f 100644 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -749,6 +749,7 @@ bld.SAMBA3_LIBRARY('smbd_base', netapi NDR_IOCTL notifyd + NDR_QUOTA ''' + bld.env['dmapi_lib'] + bld.env['legacy_quota_libs'] + -- 2.13.7 From 60c722be1ae6d197e43e7d04e0007a868ad21f40 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 8 Mar 2017 14:27:27 +0000 Subject: [PATCH 09/18] s3/script/test: modify existing smbcquota test to use SMB2 in addition to SMB1. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 6938ec995469a1b577a8781c33308fbf2bd5a669) --- 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.13.7 From 9551ec02a1d4c63433cb7083ef716fc4158410dd Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 21 Mar 2017 08:29:59 +0000 Subject: [PATCH 10/18] s3/script/tests: Add simple (smb1 & smb2) get/set/list tests for smbcquotas BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 2162b45bd27b0e2eaf73ffb71e085e61b76a973c) --- 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 39a8f49fa3a..22fe9ae3738 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -776,6 +776,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); @@ -802,6 +805,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] @@ -2018,6 +2023,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 . + +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 . + +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 . +# + +# +# Blackbox test wrapper for smbcquota +# +if [ $# -lt 6 ]; then +cat < Date: Wed, 22 Mar 2017 20:06:13 +0000 Subject: [PATCH 11/18] 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. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison (cherry picked from commit 807611fe3bc9647e82feeb1d2b2d909fa35ad8ef) --- 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 6967e4e1b37..5937380fb85 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2710,7 +2710,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.13.7 From 2f7d2bfa5c8ac2aa8f95f13811f096894959391c Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 28 Jun 2018 16:04:24 +0100 Subject: [PATCH 12/18] 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 BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Noel Power Reviewed-by: Jeremy Allison Autobuild-User(master): Noel Power Autobuild-Date(master): Tue Jul 31 19:45:59 CEST 2018 on sn-devel-144 --- 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.13.7 From 6b71f8c21cca4b27972cc6cecfdd6241412e33c7 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Tue, 7 Aug 2018 11:06:34 +0100 Subject: [PATCH 13/18] s3/smbd: Ensure quota code is only called when quota support detected BUG: https://bugzilla.samba.org/show_bug.cgi?id=13563 Signed-off-by: Noel Power Autobuild-User(v4-8-test): Karolin Seeger Autobuild-Date(v4-8-test): Mon Aug 13 17:25:23 CEST 2018 on sn-devel-144 (cherry picked from commit 6f44ef8511490f9ad9f849c09aededf0f0b38dff) --- source3/smbd/smb2_getinfo.c | 5 +++++ source3/smbd/smb2_setinfo.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/source3/smbd/smb2_getinfo.c b/source3/smbd/smb2_getinfo.c index 2b5bb0772fd..4f30a9710eb 100644 --- a/source3/smbd/smb2_getinfo.c +++ b/source3/smbd/smb2_getinfo.c @@ -523,6 +523,7 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, } case SMB2_GETINFO_QUOTA: { +#ifdef HAVE_SYS_QUOTAS struct smb2_query_quota_info info; enum ndr_err_code err; uint8_t *data = NULL; @@ -602,6 +603,10 @@ static struct tevent_req *smbd_smb2_getinfo_send(TALLOC_CTX *mem_ctx, status = NT_STATUS_OK; TALLOC_FREE(tmp_ctx); break; +#else + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); +#endif } default: diff --git a/source3/smbd/smb2_setinfo.c b/source3/smbd/smb2_setinfo.c index 266991760d6..5ea7029a1c3 100644 --- a/source3/smbd/smb2_setinfo.c +++ b/source3/smbd/smb2_setinfo.c @@ -573,6 +573,7 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, case 0x04:/* SMB2_SETINFO_QUOTA */ { +#ifdef HAVE_SYS_QUOTAS struct file_quota_information info = {0}; SMB_NTQUOTA_STRUCT qt = {0}; enum ndr_err_code err; @@ -605,6 +606,10 @@ static struct tevent_req *smbd_smb2_setinfo_send(TALLOC_CTX *mem_ctx, } status = NT_STATUS_OK; break; +#else + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); +#endif } default: tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); -- 2.13.7 From 274d4351b54399cafa610bb971294bd670bfb2cf Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 7 Aug 2018 22:48:58 +0200 Subject: [PATCH 14/18] smbd: Align integer types BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit c45fb4b658890f03a045519f95df160f27f96183) --- 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 5937380fb85..810f8b92b43 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2301,7 +2301,7 @@ static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx, struct dom_sid *sids, uint32_t elems) { - int i; + uint32_t i; TALLOC_CTX *list_ctx = NULL; list_ctx = talloc_init("quota_sid_list"); -- 2.13.7 From 60c5d1dccea648791ff30d1bee58195bd9c2fc97 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 7 Aug 2018 22:49:16 +0200 Subject: [PATCH 15/18] smbd: Fix CID 1438246 Unchecked return value BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit 2e285845ea03c5505092b8de5f6097e5845b25ec) --- source3/smbd/nttrans.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 810f8b92b43..bad904b9eb8 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2317,6 +2317,7 @@ static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx, for (i = 0; i < elems; i++) { SMB_NTQUOTA_STRUCT qt; SMB_NTQUOTA_LIST *list_item; + bool ok; if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp, SMB_USER_QUOTA_TYPE, @@ -2333,7 +2334,15 @@ static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx, return NDR_ERR_ALLOC; } - sid_to_uid(&sids[i], &list_item->uid); + ok = sid_to_uid(&sids[i], &list_item->uid); + if (!ok) { + char buf[DOM_SID_STR_BUFLEN]; + dom_sid_string_buf(&sids[i], buf, sizeof(buf)); + DBG_WARNING("Could not convert SID %s to uid\n", buf); + /* No idea what to return here... */ + return NDR_ERR_INVALID_POINTER; + } + list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT); if (list_item->quotas == NULL) { DBG_ERR("failed to allocate\n"); -- 2.13.7 From aa04e5325404a8bd1c38ef4d265ad57d2eae4c35 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 7 Aug 2018 22:50:52 +0200 Subject: [PATCH 16/18] smbd: Fix CID 1438245 Dereference before null check BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit d4e8fd5fd246a80e1a13a2bf3965d0335d4dc2c4) --- 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 bad904b9eb8..68470766f77 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -2459,7 +2459,7 @@ static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx, } } *sids = talloc_zero_array(mem_ctx, struct dom_sid, i); - if (!sids) { + if (*sids == NULL) { DBG_ERR("OOM\n"); err = NDR_ERR_ALLOC; goto done; -- 2.13.7 From 0191ab844d8e5f33f882c5c2813865ae70f1a536 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 8 Aug 2018 10:08:38 +0200 Subject: [PATCH 17/18] libsmb: Fix CID 1438244 Unsigned compared against 0 ndr_size_dom_sid returns a size_t, so that can't be <0. Also, the only case that ndr_size_dom_sid returns 0 is a NULL sid pointer. ndr_size_dom_sid can reasonably be assumed to not overflow, the number of sub-auths is a uint8. That times 4 plus 8 always fits into a size_t. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison (cherry picked from commit b1b882c2c459b206e2455f53b3926ff9a13f7335) --- source3/libsmb/cli_smb2_fnum.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c index 8df7da77b0f..8bb24535373 100644 --- a/source3/libsmb/cli_smb2_fnum.c +++ b/source3/libsmb/cli_smb2_fnum.c @@ -2878,10 +2878,6 @@ NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, sid_len = ndr_size_dom_sid(&pqt->sid, 0); 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; -- 2.13.7 From 4938fa8abc81dd44af6de3b8ec98768bb503d28c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 8 Aug 2018 10:14:26 +0200 Subject: [PATCH 18/18] libsmb: Fix CID 1438243 Unchecked return value BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553 Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Wed Aug 8 23:10:22 CEST 2018 on sn-devel-144 (cherry picked from commit 0eaf84a4f5c313776c6feab2968f241b5ce55a91) --- source3/libsmb/cliquota.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c index 8efd2bbe38a..52f98eb9e8f 100644 --- a/source3/libsmb/cliquota.c +++ b/source3/libsmb/cliquota.c @@ -649,7 +649,14 @@ NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx, /* 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); + + err = ndr_push_align(qndr, 8); + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DBG_DEBUG("ndr_push_align returned %s\n", + ndr_map_error2string(err)); + return ndr_map_error2ntstatus(err); + } + padding = qndr->offset - padding; /* -- 2.13.7