cliquota: factor out parsing of a quota record buffer
[metze/samba/wip.git] / source3 / libsmb / cliquota.c
index ce48257f001307f0bd3a7c1521f79727a5ad248d..19c2e7ea2cad9b6f089d186ae04e83fe4e4f1949 100644 (file)
@@ -23,6 +23,7 @@
 #include "fake_file.h"
 #include "../libcli/security/security.h"
 #include "trans2.h"
+#include "../libcli/smb/smbXcli_base.h"
 
 NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
 {
@@ -46,10 +47,10 @@ void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
        return; 
 }
 
-static bool parse_user_quota_record(const uint8_t *rdata,
-                                   unsigned int rdata_count,
-                                   unsigned int *offset,
-                                   SMB_NTQUOTA_STRUCT *pqt)
+bool parse_user_quota_record(const uint8_t *rdata,
+                            unsigned int rdata_count,
+                            unsigned int *offset,
+                            SMB_NTQUOTA_STRUCT *pqt)
 {
        int sid_len;
        SMB_NTQUOTA_STRUCT qt;
@@ -72,11 +73,18 @@ static bool parse_user_quota_record(const uint8_t *rdata,
 
        /* sid len */
        sid_len = IVAL(rdata,4);
+       if (40 + sid_len < 40) {
+               return false;
+       }
 
        if (rdata_count < 40+sid_len) {
                return False;           
        }
 
+       if (*offset != 0 && *offset < 40 + sid_len) {
+               return false;
+       }
+
        /* unknown 8 bytes in pdata 
         * maybe its the change time in NTTIME
         */
@@ -101,6 +109,65 @@ static bool parse_user_quota_record(const uint8_t *rdata,
        return True;
 }
 
+NTSTATUS parse_user_quota_list(const uint8_t *curdata,
+                              uint32_t curdata_count,
+                              TALLOC_CTX *mem_ctx,
+                              SMB_NTQUOTA_LIST **pqt_list)
+{
+       NTSTATUS status = NT_STATUS_OK;
+       unsigned offset;
+       SMB_NTQUOTA_STRUCT qt;
+       SMB_NTQUOTA_LIST *tmp_list_ent;
+
+       while (true) {
+               ZERO_STRUCT(qt);
+               if (!parse_user_quota_record(curdata, curdata_count, &offset,
+                                            &qt)) {
+                       DEBUG(1, ("Failed to parse the quota record\n"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       break;
+               }
+
+               if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) ==
+                   NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       break;
+               }
+
+               if ((tmp_list_ent->quotas =
+                        talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       break;
+               }
+
+               memcpy(tmp_list_ent->quotas, &qt, sizeof(qt));
+               tmp_list_ent->mem_ctx = mem_ctx;
+
+               DLIST_ADD((*pqt_list), tmp_list_ent);
+
+               if (offset > curdata_count) {
+                       DEBUG(1, ("out of bounds offset in quota record\n"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       break;
+               }
+
+               if (curdata + offset < curdata) {
+                       DEBUG(1, ("Pointer overflow in quota record\n"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       break;
+               }
+
+               curdata += offset;
+               curdata_count -= offset;
+
+               if (offset == 0) {
+                       break;
+               }
+       }
+
+       return status;
+}
+
 NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
                            SMB_NTQUOTA_STRUCT *pqt)
 {
@@ -118,6 +185,10 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
                smb_panic("cli_get_user_quota() called with NULL Pointer!");
        }
 
+       if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
+               return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
+       }
+
        SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
 
        SSVAL(params, 0,quota_fnum);
@@ -205,29 +276,24 @@ NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
        return status;
 }
 
-NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
-                            SMB_NTQUOTA_LIST **pqt_list)
+static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
+                                        TALLOC_CTX *mem_ctx,
+                                        int quota_fnum,
+                                        SMB_NTQUOTA_LIST **pqt_list,
+                                        bool first)
 {
        uint16_t setup[1];
        uint8_t params[16];
        uint8_t *rparam=NULL, *rdata=NULL;
        uint32_t rparam_count=0, rdata_count=0;
-       unsigned int offset;
-       const uint8_t *curdata = NULL;
-       unsigned int curdata_count = 0;
-       TALLOC_CTX *mem_ctx = NULL;
-       SMB_NTQUOTA_STRUCT qt;
-       SMB_NTQUOTA_LIST *tmp_list_ent;
        NTSTATUS status;
-
-       if (!cli||!pqt_list) {
-               smb_panic("cli_list_user_quota() called with NULL Pointer!");
-       }
+       uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
+                           : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
 
        SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
 
        SSVAL(params, 0,quota_fnum);
-       SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
+       SSVAL(params, 2, op);
        SIVAL(params, 4,0x00000000);
        SIVAL(params, 8,0x00000000);
        SIVAL(params,12,0x00000000);
@@ -243,117 +309,57 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
                           &rparam, 0, &rparam_count,
                           &rdata, 0, &rdata_count);
 
-       if (!NT_STATUS_IS_OK(status) &&
-           !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
-               DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
-                         nt_errstr(status)));
-               goto cleanup;
-       }
-
-       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
-           rdata_count == 0) {
-               *pqt_list = NULL;
-               return NT_STATUS_OK;
+       /* compat. with smbd + safeguard against
+        * endless loop
+        */
+       if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
+               status = NT_STATUS_NO_MORE_ENTRIES;
        }
 
-       if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
-               DEBUG(0,("talloc_init() failed\n"));
-               return NT_STATUS_NO_MEMORY;
+       if (!NT_STATUS_IS_OK(status)) {
+               goto cleanup;
        }
 
-       offset = 1;
-       for (curdata=rdata,curdata_count=rdata_count;
-               ((curdata)&&(curdata_count>=8)&&(offset>0));
-               curdata +=offset,curdata_count -= offset) {
-               ZERO_STRUCT(qt);
-               if (!parse_user_quota_record((const uint8_t *)curdata, curdata_count,
-                                            &offset, &qt)) {
-                       DEBUG(1,("Failed to parse the quota record\n"));
-                       goto cleanup;
-               }
+       status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
 
-               if ((tmp_list_ent=talloc_zero(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
-                       DEBUG(0,("TALLOC_ZERO() failed\n"));
-                       talloc_destroy(mem_ctx);
-                       return NT_STATUS_NO_MEMORY;
-               }
+cleanup:
+       TALLOC_FREE(rparam);
+       TALLOC_FREE(rdata);
 
-               if ((tmp_list_ent->quotas=talloc_zero(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
-                       DEBUG(0,("TALLOC_ZERO() failed\n"));
-                       talloc_destroy(mem_ctx);
-                       return NT_STATUS_NO_MEMORY;
-               }
+       return status;
+}
 
-               memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
-               tmp_list_ent->mem_ctx = mem_ctx;                
+NTSTATUS cli_list_user_quota(struct cli_state *cli,
+                            int quota_fnum,
+                            SMB_NTQUOTA_LIST **pqt_list)
+{
+       NTSTATUS status;
+       TALLOC_CTX *mem_ctx = NULL;
+       bool first = true;
 
-               DLIST_ADD((*pqt_list),tmp_list_ent);
+       if (!cli || !pqt_list) {
+               smb_panic("cli_list_user_quota() called with NULL Pointer!");
        }
 
-       SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE); 
-       while(1) {
-
-               TALLOC_FREE(rparam);
-               TALLOC_FREE(rdata);
-
-               status = cli_trans(talloc_tos(), cli, SMBnttrans,
-                                  NULL, -1, /* name, fid */
-                                  NT_TRANSACT_GET_USER_QUOTA, 0,
-                                  setup, 1, 0, /* setup */
-                                  params, 16, 4, /* params */
-                                  NULL, 0, 2048, /* data */
-                                  NULL,                /* recv_flags2 */
-                                  NULL, 0, NULL,       /* rsetup */
-                                  &rparam, 0, &rparam_count,
-                                  &rdata, 0, &rdata_count);
-
-               if (!NT_STATUS_IS_OK(status) &&
-                   !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
-                       DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
-                                 nt_errstr(status)));
-                       goto cleanup;
-               }
+       *pqt_list = NULL;
 
-               if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES) ||
-                   rdata_count == 0) {
-                       status = NT_STATUS_OK;
-                       break;
-               }
+       if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
 
-               offset = 1;
-               for (curdata=rdata,curdata_count=rdata_count;
-                       ((curdata)&&(curdata_count>=8)&&(offset>0));
-                       curdata +=offset,curdata_count -= offset) {
-                       ZERO_STRUCT(qt);
-                       if (!parse_user_quota_record((const uint8_t *)curdata,
-                                                    curdata_count, &offset,
-                                                    &qt)) {
-                               DEBUG(1,("Failed to parse the quota record\n"));
-                               goto cleanup;
-                       }
-
-                       if ((tmp_list_ent=talloc_zero(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
-                               DEBUG(0,("TALLOC_ZERO() failed\n"));
-                               talloc_destroy(mem_ctx);
-                               goto cleanup;
-                       }
-
-                       if ((tmp_list_ent->quotas=talloc_zero(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
-                               DEBUG(0,("TALLOC_ZERO() failed\n"));
-                               talloc_destroy(mem_ctx);
-                               goto cleanup;
-                       }
-
-                       memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
-                       tmp_list_ent->mem_ctx = mem_ctx;                
-
-                       DLIST_ADD((*pqt_list),tmp_list_ent);
-               }
+       do {
+               status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
+                                                 pqt_list, first);
+               first = false;
+       } while (NT_STATUS_IS_OK(status));
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+               status = NT_STATUS_OK;
        }
 
- cleanup:
-       TALLOC_FREE(rparam);
-       TALLOC_FREE(rdata);
+       if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
+               TALLOC_FREE(mem_ctx);
+       }
 
        return status;
 }