cliquota: some security hardening
[metze/samba/wip.git] / source3 / libsmb / cliquota.c
index 9136506e48c2307b3188928bedf3669a13ed7665..e4589c47655e44c230e8d62144e49dda464ed06f 100644 (file)
 #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)
 {
        return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32,
                 0x00000016, DESIRED_ACCESS_PIPE,
                 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
-                FILE_OPEN, 0x00000000, 0x03, quota_fnum);
+                FILE_OPEN, 0x00000000, 0x03, quota_fnum, NULL);
 }
 
 void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
 {
-       if (!qt_list)
+       if (!qt_list || !*qt_list) {
                return;
+       }
 
        if ((*qt_list)->mem_ctx)
                talloc_destroy((*qt_list)->mem_ctx);
@@ -45,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;
@@ -71,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
         */
@@ -89,7 +98,7 @@ static bool parse_user_quota_record(const uint8_t *rdata,
        /* the hard quotas 8 bytes (uint64_t)*/
        qt.hardlim = BVAL(rdata,32);
 
-       if (!sid_parse((const char *)rdata+40,sid_len,&qt.sid)) {
+       if (!sid_parse(rdata+40,sid_len,&qt.sid)) {
                return false;
        }
 
@@ -117,6 +126,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);
@@ -129,7 +142,7 @@ NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
        data_len = sid_len+8;
        SIVAL(data, 0, 0x00000000);
        SIVAL(data, 4, sid_len);
-       sid_linearize((char *)data+8, sid_len, &pqt->sid);
+       sid_linearize(data+8, sid_len, &pqt->sid);
 
        status = cli_trans(talloc_tos(), cli, SMBnttrans,
                           NULL, -1, /* name, fid */
@@ -183,7 +196,7 @@ NTSTATUS cli_set_user_quota(struct cli_state *cli, int quota_fnum,
        SBIG_UINT(data,16,pqt->usedspace);
        SBIG_UINT(data,24,pqt->softlim);
        SBIG_UINT(data,32,pqt->hardlim);
-       sid_linearize((char *)data+40, sid_len, &pqt->sid);
+       sid_linearize(data+40, sid_len, &pqt->sid);
 
        status = cli_trans(talloc_tos(), cli, SMBnttrans,
                           NULL, -1, /* name, fid */
@@ -204,8 +217,11 @@ 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];
@@ -214,19 +230,16 @@ NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum,
        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);
@@ -242,116 +255,106 @@ 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)) {
-               DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
-                         nt_errstr(status)));
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
                goto cleanup;
        }
 
+       /* compat. with smbd + safeguard against
+        * endless loop
+        */
        if (rdata_count == 0) {
-               *pqt_list = NULL;
-               return NT_STATUS_OK;
-       }
-
-       if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
-               DEBUG(0,("talloc_init() failed\n"));
-               return NT_STATUS_NO_MEMORY;
+               status = NT_STATUS_NO_MORE_ENTRIES;
        }
 
-       offset = 1;
-       for (curdata=rdata,curdata_count=rdata_count;
-               ((curdata)&&(curdata_count>=8)&&(offset>0));
-               curdata +=offset,curdata_count -= offset) {
+       curdata = rdata;
+       curdata_count = rdata_count;
+       while (true) {
                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"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                        goto cleanup;
                }
 
                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;
+                       status = NT_STATUS_NO_MEMORY;
+                       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);
-                       return NT_STATUS_NO_MEMORY;
+                       status = NT_STATUS_NO_MEMORY;
+                       goto cleanup;
                }
 
                memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
                tmp_list_ent->mem_ctx = mem_ctx;                
 
                DLIST_ADD((*pqt_list),tmp_list_ent);
-       }
 
-       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)) {
-                       DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
-                                 nt_errstr(status)));
+               if (offset > curdata_count) {
+                       DEBUG(1, ("out of bounds offset in quota record\n"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
                        goto cleanup;
                }
 
-               if (rdata_count == 0) {
-                       break;
+               if (curdata + offset < curdata) {
+                       DEBUG(1, ("Pointer overflow in quota record\n"));
+                       status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+                       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;
-                       }
-
-                       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);
+               curdata += offset;
+               curdata_count -= offset;
+
+               if (offset == 0) {
+                       break;
                }
        }
 
- cleanup:
+cleanup:
        TALLOC_FREE(rparam);
        TALLOC_FREE(rdata);
 
        return status;
 }
 
+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;
+
+       if (!cli || !pqt_list) {
+               smb_panic("cli_list_user_quota() called with NULL Pointer!");
+       }
+
+       *pqt_list = NULL;
+
+       if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       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;
+       }
+
+       if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
+               TALLOC_FREE(mem_ctx);
+       }
+
+       return status;
+}
+
 NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
                               SMB_NTQUOTA_STRUCT *pqt)
 {
@@ -445,7 +448,7 @@ NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
                           NULL, -1, /* name, fid */
                           0, 0,     /* function, flags */
                           setup, 1, 0, /* setup */
-                          param, 8, 0, /* param */
+                          param, 4, 0, /* param */
                           data, 48, 0, /* data */
                           NULL,         /* recv_flags2 */
                           NULL, 0, NULL, /* rsetup */