s3/smbd: adjust smb1 server to use idl structs and generated ndr push/pull funcs
authorNoel Power <noel.power@suse.com>
Fri, 26 May 2017 15:01:53 +0000 (16:01 +0100)
committerNoel Power <npower@samba.org>
Tue, 31 Jul 2018 14:56:25 +0000 (16:56 +0200)
BUG: https://bugzilla.samba.org/show_bug.cgi?id=13553

Signed-off-by: Noel Power <noel.power@suse.com>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/libsmb/cliquota.c
source3/smbd/nttrans.c
source3/smbd/proto.h

index 7e8565b44b16fd41f7faec2088f598c715500bb1..8efd2bbe38added28d070e084bd0050a8f837dc4 100644 (file)
@@ -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(
+               &param_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 */
index ca02dbcc3d9969f6de3c46c8983143c0049392a4..6967e4e1b37155de413b8fb21d16b7b5402b8146 100644 (file)
@@ -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,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;
+               }
 
-                       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,
+                                  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);
 }
 
 /****************************************************************************
@@ -2592,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 */
@@ -2603,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;
        }
 
        /*
@@ -2613,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 */
 
index 2980935e59908861baa3cf9dfdda20e7c5676dce..2a41d9d251ddc78e3af59357baa3f4a7f69a6438 100644 (file)
@@ -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);