s3:smbd: fix max_buffer handling of initial notify requests
[samba.git] / source3 / smbd / nttrans.c
index 232cc687b504fdba4146e817b3c8fe2756428532..8bb121a13515fe6c43a36cf9550485d642200e94 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;
 
@@ -461,8 +463,7 @@ void reply_ntcreate_and_X(struct smb_request *req)
        int oplock_request;
        uint8_t oplock_granted = NO_OPLOCK_RETURN;
        struct case_semantics_state *case_state = NULL;
-       uint32_t ucf_flags = UCF_PREP_CREATEFILE |
-                       (req->posix_pathnames ? UCF_POSIX_PATHNAMES : 0);
+       uint32_t ucf_flags;
        TALLOC_CTX *ctx = talloc_tos();
 
        START_PROFILE(SMBntcreateX);
@@ -536,12 +537,13 @@ void reply_ntcreate_and_X(struct smb_request *req)
                }
        }
 
+       ucf_flags = filename_create_ucf_flags(req, create_disposition);
        status = filename_convert(ctx,
                                conn,
-                               req->flags2 & FLAGS2_DFS_PATHNAMES,
                                fname,
                                ucf_flags,
                                NULL,
+                               NULL,
                                &smb_fname);
 
        TALLOC_FREE(case_state);
@@ -688,16 +690,19 @@ void reply_ntcreate_and_X(struct smb_request *req)
        p += 8;
        if (flags & EXTENDED_RESPONSE_REQUIRED) {
                uint16_t file_status = (NO_EAS|NO_SUBSTREAMS|NO_REPARSETAG);
-               size_t num_names = 0;
                unsigned int num_streams = 0;
                struct stream_struct *streams = NULL;
 
-               /* Do we have any EA's ? */
-               status = get_ea_names_from_file(ctx, conn, fsp,
-                               smb_fname, NULL, &num_names);
-               if (NT_STATUS_IS_OK(status) && num_names) {
-                       file_status &= ~NO_EAS;
+               if (lp_ea_support(SNUM(conn))) {
+                       size_t num_names = 0;
+                       /* Do we have any EA's ? */
+                       status = get_ea_names_from_file(
+                           ctx, conn, fsp, smb_fname, NULL, &num_names);
+                       if (NT_STATUS_IS_OK(status) && num_names) {
+                               file_status &= ~NO_EAS;
+                       }
                }
+
                status = vfs_streaminfo(conn, NULL, smb_fname, ctx,
                        &num_streams, &streams);
                /* There is always one stream, ::$DATA. */
@@ -1024,8 +1029,7 @@ static void call_nt_transact_create(connection_struct *conn,
        int oplock_request;
        uint8_t oplock_granted;
        struct case_semantics_state *case_state = NULL;
-       uint32_t ucf_flags = UCF_PREP_CREATEFILE |
-                       (req->posix_pathnames ? UCF_POSIX_PATHNAMES : 0);
+       uint32_t ucf_flags;
        TALLOC_CTX *ctx = talloc_tos();
 
        DEBUG(5,("call_nt_transact_create\n"));
@@ -1106,12 +1110,13 @@ static void call_nt_transact_create(connection_struct *conn,
                }
        }
 
+       ucf_flags = filename_create_ucf_flags(req, create_disposition);
        status = filename_convert(ctx,
                                conn,
-                               req->flags2 & FLAGS2_DFS_PATHNAMES,
                                fname,
                                ucf_flags,
                                NULL,
+                               NULL,
                                &smb_fname);
 
        TALLOC_FREE(case_state);
@@ -1334,16 +1339,19 @@ static void call_nt_transact_create(connection_struct *conn,
        p += 8;
        if (flags & EXTENDED_RESPONSE_REQUIRED) {
                uint16_t file_status = (NO_EAS|NO_SUBSTREAMS|NO_REPARSETAG);
-               size_t num_names = 0;
                unsigned int num_streams = 0;
                struct stream_struct *streams = NULL;
 
-               /* Do we have any EA's ? */
-               status = get_ea_names_from_file(ctx, conn, fsp,
-                               smb_fname, NULL, &num_names);
-               if (NT_STATUS_IS_OK(status) && num_names) {
-                       file_status &= ~NO_EAS;
+               if (lp_ea_support(SNUM(conn))) {
+                       size_t num_names = 0;
+                       /* Do we have any EA's ? */
+                       status = get_ea_names_from_file(
+                           ctx, conn, fsp, smb_fname, NULL, &num_names);
+                       if (NT_STATUS_IS_OK(status) && num_names) {
+                               file_status &= ~NO_EAS;
+                       }
                }
+
                status = vfs_streaminfo(conn, NULL, smb_fname, ctx,
                        &num_streams, &streams);
                /* There is always one stream, ::$DATA. */
@@ -1567,8 +1575,8 @@ void reply_ntrename(struct smb_request *req)
        bool src_has_wcard = False;
        bool dest_has_wcard = False;
        uint32_t attrs;
-       uint32_t ucf_flags_src = (req->posix_pathnames ? UCF_POSIX_PATHNAMES : 0);
-       uint32_t ucf_flags_dst = (req->posix_pathnames ? UCF_POSIX_PATHNAMES : 0);
+       uint32_t ucf_flags_src = ucf_flags_from_smb_request(req);
+       uint32_t ucf_flags_dst = ucf_flags_from_smb_request(req);
        uint16_t rename_type;
        TALLOC_CTX *ctx = talloc_tos();
        bool stream_rename = false;
@@ -1621,16 +1629,16 @@ void reply_ntrename(struct smb_request *req)
         * destination's last component.
         */
        if (rename_type == RENAME_FLAG_RENAME) {
-               ucf_flags_src = UCF_COND_ALLOW_WCARD_LCOMP;
-               ucf_flags_dst = UCF_COND_ALLOW_WCARD_LCOMP | UCF_SAVE_LCOMP;
+               ucf_flags_src |= UCF_COND_ALLOW_WCARD_LCOMP;
+               ucf_flags_dst |= UCF_COND_ALLOW_WCARD_LCOMP | UCF_SAVE_LCOMP;
        }
 
        /* rename_internals() calls unix_convert(), so don't call it here. */
        status = filename_convert(ctx, conn,
-                                 req->flags2 & FLAGS2_DFS_PATHNAMES,
                                  oldname,
                                  ucf_flags_src,
                                  NULL,
+                                 NULL,
                                  &smb_fname_old);
        if (!NT_STATUS_IS_OK(status)) {
                if (NT_STATUS_EQUAL(status,
@@ -1645,9 +1653,9 @@ void reply_ntrename(struct smb_request *req)
        }
 
        status = filename_convert(ctx, conn,
-                                 req->flags2 & FLAGS2_DFS_PATHNAMES,
                                  newname,
                                  ucf_flags_dst,
+                                 NULL,
                                  &dest_has_wcard,
                                  &smb_fname_new);
        if (!NT_STATUS_IS_OK(status)) {
@@ -1798,8 +1806,10 @@ static void call_nt_transact_notify_change(connection_struct *conn,
 
        if (fsp->notify == NULL) {
 
-               status = change_notify_create(fsp, filter, recursive);
-
+               status = change_notify_create(fsp,
+                                             max_param_count,
+                                             filter,
+                                             recursive);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(10, ("change_notify_create returned %s\n",
                                   nt_errstr(status)));
@@ -2291,288 +2301,401 @@ 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;
-       }
+       uint32_t 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;
+               bool ok;
 
-       /* 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;
+               ok = sid_to_uid(&sids[i], &list_item->uid);
+               if (!ok) {
+                       struct dom_sid_buf buf;
+                       DBG_WARNING("Could not convert SID %s to uid\n",
+                                   dom_sid_str_buf(&sids[i], &buf));
+                       /* No idea what to return here... */
+                       return NDR_ERR_INVALID_POINTER;
+               }
 
-               case TRANSACT_GET_USER_QUOTA_LIST_START:
+               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;
+               }
 
-                       if (qt_handle->quota_list==NULL &&
-                               qt_handle->tmp_list==NULL) {
-                               start_enum = True;
-                       }
+               *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 (start_enum && vfs_get_user_ntquota_list(fsp,&(qt_handle->quota_list))!=0) {
-                               reply_nterror(req, NT_STATUS_INTERNAL_ERROR);
-                               return;
-                       }
+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;
+       }
 
-                       /* 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;
-                       }
+       *num = 0;
+       *sids = NULL;
 
-                       /* 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;
+                       offset += new_offset;
 
-                       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;
-                       }
-
-                       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 == NULL) {
+                       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)) {
-                               ZERO_STRUCT(qt);
-                               /*
-                                * we have to return zero's in all fields
-                                * instead of returning an error here
-                                * --metze
-                                */
-                       }
+               *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++) {
+                       struct dom_sid_buf buf;
+                       (*sids)[i] = iter->sid;
+                       DBG_DEBUG("quota SID[%u] %s\n",
+                               (unsigned int)i,
+                               dom_sid_str_buf(&iter->sid, &buf));
+               }
+       }
+       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);
 }
 
 /****************************************************************************
@@ -2593,19 +2716,22 @@ 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 */
-       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));
-               reply_nterror(req, NT_STATUS_ACCESS_DENIED);
-               return;
+               status = NT_STATUS_ACCESS_DENIED;
+               goto error;
        }
 
        /*
@@ -2614,67 +2740,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 */