cliquota: factor out building of FILE_QUOTA_INFORMATION
[metze/samba/wip.git] / source3 / libsmb / cliquota.c
1 /* 
2    Unix SMB/CIFS implementation.
3    client quota functions
4    Copyright (C) Stefan (metze) Metzmacher      2003
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "libsmb/libsmb.h"
22 #include "../librpc/gen_ndr/ndr_security.h"
23 #include "fake_file.h"
24 #include "../libcli/security/security.h"
25 #include "trans2.h"
26 #include "../libcli/smb/smbXcli_base.h"
27
28 NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum)
29 {
30         return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32,
31                  0x00000016, DESIRED_ACCESS_PIPE,
32                  0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
33                  FILE_OPEN, 0x00000000, 0x03, quota_fnum, NULL);
34 }
35
36 void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
37 {
38         if (!qt_list || !*qt_list) {
39                 return;
40         }
41
42         if ((*qt_list)->mem_ctx)
43                 talloc_destroy((*qt_list)->mem_ctx);
44
45         (*qt_list) = NULL;
46
47         return; 
48 }
49
50 bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx,
51                                 SMB_NTQUOTA_STRUCT *pqt,
52                                 SMB_NTQUOTA_LIST **pqt_list)
53 {
54         SMB_NTQUOTA_LIST *tmp_list_ent;
55
56         if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) == NULL) {
57                 return false;
58         }
59
60         if ((tmp_list_ent->quotas = talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) ==
61             NULL) {
62                 return false;
63         }
64
65         *tmp_list_ent->quotas = *pqt;
66         tmp_list_ent->mem_ctx = mem_ctx;
67
68         DLIST_ADD((*pqt_list), tmp_list_ent);
69
70         return true;
71 }
72
73 bool parse_user_quota_record(const uint8_t *rdata,
74                              unsigned int rdata_count,
75                              unsigned int *offset,
76                              SMB_NTQUOTA_STRUCT *pqt)
77 {
78         int sid_len;
79         SMB_NTQUOTA_STRUCT qt;
80
81         ZERO_STRUCT(qt);
82
83         if (!rdata||!offset||!pqt) {
84                 smb_panic("parse_quota_record: called with NULL POINTER!");
85         }
86
87         if (rdata_count < 40) {
88                 return False;
89         }
90
91         /* offset to next quota record.
92          * 4 bytes IVAL(rdata,0)
93          * unused here...
94          */
95         *offset = IVAL(rdata,0);
96
97         /* sid len */
98         sid_len = IVAL(rdata,4);
99         if (40 + sid_len < 40) {
100                 return false;
101         }
102
103         if (rdata_count < 40+sid_len) {
104                 return False;           
105         }
106
107         if (*offset != 0 && *offset < 40 + sid_len) {
108                 return false;
109         }
110
111         /* unknown 8 bytes in pdata 
112          * maybe its the change time in NTTIME
113          */
114
115         /* the used space 8 bytes (uint64_t)*/
116         qt.usedspace = BVAL(rdata,16);
117
118         /* the soft quotas 8 bytes (uint64_t)*/
119         qt.softlim = BVAL(rdata,24);
120
121         /* the hard quotas 8 bytes (uint64_t)*/
122         qt.hardlim = BVAL(rdata,32);
123
124         if (!sid_parse(rdata+40,sid_len,&qt.sid)) {
125                 return false;
126         }
127
128         qt.qtype = SMB_USER_QUOTA_TYPE;
129
130         *pqt = qt;
131
132         return True;
133 }
134
135 NTSTATUS parse_user_quota_list(const uint8_t *curdata,
136                                uint32_t curdata_count,
137                                TALLOC_CTX *mem_ctx,
138                                SMB_NTQUOTA_LIST **pqt_list)
139 {
140         NTSTATUS status = NT_STATUS_OK;
141         unsigned offset;
142         SMB_NTQUOTA_STRUCT qt;
143
144         while (true) {
145                 ZERO_STRUCT(qt);
146                 if (!parse_user_quota_record(curdata, curdata_count, &offset,
147                                              &qt)) {
148                         DEBUG(1, ("Failed to parse the quota record\n"));
149                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
150                         break;
151                 }
152
153                 if (offset > curdata_count) {
154                         DEBUG(1, ("out of bounds offset in quota record\n"));
155                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
156                         break;
157                 }
158
159                 if (curdata + offset < curdata) {
160                         DEBUG(1, ("Pointer overflow in quota record\n"));
161                         status = NT_STATUS_INVALID_NETWORK_RESPONSE;
162                         break;
163                 }
164
165                 if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) {
166                         status = NT_STATUS_NO_MEMORY;
167                         break;
168                 }
169
170                 curdata += offset;
171                 curdata_count -= offset;
172
173                 if (offset == 0) {
174                         break;
175                 }
176         }
177
178         return status;
179 }
180
181 NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata,
182                                unsigned int rdata_count,
183                                SMB_NTQUOTA_STRUCT *pqt)
184 {
185         SMB_NTQUOTA_STRUCT qt;
186
187         ZERO_STRUCT(qt);
188
189         if (rdata_count < 48) {
190                 /* minimum length is not enforced by SMB2 client.
191                  */
192                 DEBUG(1, ("small returned fs quota buffer\n"));
193                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
194         }
195
196         /* unknown_1 24 NULL bytes in pdata*/
197
198         /* the soft quotas 8 bytes (uint64_t)*/
199         qt.softlim = BVAL(rdata, 24);
200
201         /* the hard quotas 8 bytes (uint64_t)*/
202         qt.hardlim = BVAL(rdata, 32);
203
204         /* quota_flags 2 bytes **/
205         qt.qflags = SVAL(rdata, 40);
206
207         qt.qtype = SMB_USER_FS_QUOTA_TYPE;
208
209         *pqt = qt;
210
211         return NT_STATUS_OK;
212 }
213
214 NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list,
215                                  uint32_t maxlen,
216                                  TALLOC_CTX *mem_ctx,
217                                  DATA_BLOB *outbuf,
218                                  SMB_NTQUOTA_LIST **end_ptr)
219 {
220         uint32_t qt_len = 0;
221         uint8_t *entry;
222         uint32_t entry_len;
223         int sid_len;
224         SMB_NTQUOTA_LIST *qtl;
225         DATA_BLOB qbuf = data_blob_null;
226         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
227
228         if (qt_list == NULL) {
229                 status = NT_STATUS_OK;
230                 *outbuf = data_blob_null;
231                 if (end_ptr) {
232                         *end_ptr = NULL;
233                 }
234                 return NT_STATUS_OK;
235         }
236
237         for (qtl = qt_list; qtl != NULL; qtl = qtl->next) {
238
239                 sid_len = ndr_size_dom_sid(&qtl->quotas->sid, 0);
240                 if (47 + sid_len < 47) {
241                         status = NT_STATUS_INVALID_PARAMETER;
242                         goto fail;
243                 }
244                 entry_len = 40 + sid_len;
245                 entry_len = ((entry_len + 7) / 8) * 8;
246
247                 if (qt_len + entry_len < qt_len) {
248                         status = NT_STATUS_INVALID_PARAMETER;
249                         goto fail;
250                 }
251                 qt_len += entry_len;
252         }
253
254         if (maxlen > 0 && qt_len > maxlen) {
255                 qt_len = maxlen;
256         }
257
258         qbuf = data_blob_talloc_zero(mem_ctx, qt_len);
259         if (qbuf.data == NULL) {
260                 status = NT_STATUS_NO_MEMORY;
261                 goto fail;
262         }
263
264         for (qt_len = 0, entry = qbuf.data; qt_list != NULL;
265              qt_list = qt_list->next, qt_len += entry_len, entry += entry_len) {
266
267                 sid_len = ndr_size_dom_sid(&qt_list->quotas->sid, 0);
268                 entry_len = 40 + sid_len;
269                 entry_len = ((entry_len + 7) / 8) * 8;
270
271                 if (qt_len + entry_len > qbuf.length) {
272                         /* check for not-enough room even for a single
273                          * entry
274                          */
275                         if (qt_len == 0) {
276                                 status = NT_STATUS_BUFFER_TOO_SMALL;
277                                 goto fail;
278                         }
279
280                         break;
281                 }
282
283                 /* nextoffset entry 4 bytes */
284                 SIVAL(entry, 0, entry_len);
285
286                 /* then the len of the SID 4 bytes */
287                 SIVAL(entry, 4, sid_len);
288
289                 /* NTTIME of last record change */
290                 SBIG_UINT(entry, 8, (uint64_t)0);
291
292                 /* the used disk space 8 bytes uint64_t */
293                 SBIG_UINT(entry, 16, qt_list->quotas->usedspace);
294
295                 /* the soft quotas 8 bytes uint64_t */
296                 SBIG_UINT(entry, 24, qt_list->quotas->softlim);
297
298                 /* the hard quotas 8 bytes uint64_t */
299                 SBIG_UINT(entry, 32, qt_list->quotas->hardlim);
300
301                 /* and now the SID */
302                 sid_linearize((uint8_t *)(entry + 40), sid_len,
303                               &qt_list->quotas->sid);
304         }
305
306         /* overwrite the offset of the last entry */
307         SIVAL(entry - entry_len, 0, 0);
308
309         /*potentially shrink the buffer if max was given
310          * and we haven't quite reached the max
311          */
312         qbuf.length = qt_len;
313         *outbuf = qbuf;
314         qbuf = data_blob_null;
315         status = NT_STATUS_OK;
316
317         if (end_ptr) {
318                 *end_ptr = qt_list;
319         }
320
321 fail:
322         data_blob_free(&qbuf);
323
324         return status;
325 }
326
327 NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
328                             SMB_NTQUOTA_STRUCT *pqt)
329 {
330         uint16_t setup[1];
331         uint8_t params[16];
332         unsigned int data_len;
333         uint8_t data[SID_MAX_SIZE+8];
334         uint8_t *rparam, *rdata;
335         uint32_t rparam_count, rdata_count;
336         unsigned int sid_len;
337         unsigned int offset;
338         NTSTATUS status;
339
340         if (!cli||!pqt) {
341                 smb_panic("cli_get_user_quota() called with NULL Pointer!");
342         }
343
344         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
345                 return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
346         }
347
348         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
349
350         SSVAL(params, 0,quota_fnum);
351         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
352         SIVAL(params, 4,0x00000024);
353         SIVAL(params, 8,0x00000000);
354         SIVAL(params,12,0x00000024);
355
356         sid_len = ndr_size_dom_sid(&pqt->sid, 0);
357         data_len = sid_len+8;
358         SIVAL(data, 0, 0x00000000);
359         SIVAL(data, 4, sid_len);
360         sid_linearize(data+8, sid_len, &pqt->sid);
361
362         status = cli_trans(talloc_tos(), cli, SMBnttrans,
363                            NULL, -1, /* name, fid */
364                            NT_TRANSACT_GET_USER_QUOTA, 0,
365                            setup, 1, 0, /* setup */
366                            params, 16, 4, /* params */
367                            data, data_len, 112, /* data */
368                            NULL,                /* recv_flags2 */
369                            NULL, 0, NULL,       /* rsetup */
370                            &rparam, 4, &rparam_count,
371                            &rdata, 8, &rdata_count);
372         if (!NT_STATUS_IS_OK(status)) {
373                 DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
374                           nt_errstr(status)));
375                 return status;
376         }
377
378         if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
379                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
380                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
381         }
382
383         TALLOC_FREE(rparam);
384         TALLOC_FREE(rdata);
385         return status;
386 }
387
388 NTSTATUS
389 cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
390 {
391         uint16_t setup[1];
392         uint8_t params[2];
393         DATA_BLOB data = data_blob_null;
394         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
395
396         if (!cli || !qtl) {
397                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
398         }
399
400         status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
401         if (!NT_STATUS_IS_OK(status)) {
402                 goto cleanup;
403         }
404
405         SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
406
407         SSVAL(params,0,quota_fnum);
408
409         status = cli_trans(talloc_tos(), cli, SMBnttrans,
410                            NULL, -1, /* name, fid */
411                            NT_TRANSACT_SET_USER_QUOTA, 0,
412                            setup, 1, 0, /* setup */
413                            params, 2, 0, /* params */
414                            data.data, data.length, 0, /* data */
415                            NULL,                /* recv_flags2 */
416                            NULL, 0, NULL,       /* rsetup */
417                            NULL, 0, NULL,       /* rparams */
418                            NULL, 0, NULL);      /* rdata */
419
420         if (!NT_STATUS_IS_OK(status)) {
421                 DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n",
422                           nt_errstr(status)));
423         }
424
425 cleanup:
426         data_blob_free(&data);
427         return status;
428 }
429
430 static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
431                                          TALLOC_CTX *mem_ctx,
432                                          int quota_fnum,
433                                          SMB_NTQUOTA_LIST **pqt_list,
434                                          bool first)
435 {
436         uint16_t setup[1];
437         uint8_t params[16];
438         uint8_t *rparam=NULL, *rdata=NULL;
439         uint32_t rparam_count=0, rdata_count=0;
440         NTSTATUS status;
441         uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
442                             : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
443
444         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
445                 return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
446                                                      pqt_list, first);
447         }
448
449         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
450
451         SSVAL(params, 0,quota_fnum);
452         SSVAL(params, 2, op);
453         SIVAL(params, 4,0x00000000);
454         SIVAL(params, 8,0x00000000);
455         SIVAL(params,12,0x00000000);
456
457         status = cli_trans(talloc_tos(), cli, SMBnttrans,
458                            NULL, -1, /* name, fid */
459                            NT_TRANSACT_GET_USER_QUOTA, 0,
460                            setup, 1, 0, /* setup */
461                            params, 16, 4, /* params */
462                            NULL, 0, 2048, /* data */
463                            NULL,                /* recv_flags2 */
464                            NULL, 0, NULL,       /* rsetup */
465                            &rparam, 0, &rparam_count,
466                            &rdata, 0, &rdata_count);
467
468         /* compat. with smbd + safeguard against
469          * endless loop
470          */
471         if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
472                 status = NT_STATUS_NO_MORE_ENTRIES;
473         }
474
475         if (!NT_STATUS_IS_OK(status)) {
476                 goto cleanup;
477         }
478
479         status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
480
481 cleanup:
482         TALLOC_FREE(rparam);
483         TALLOC_FREE(rdata);
484
485         return status;
486 }
487
488 NTSTATUS cli_list_user_quota(struct cli_state *cli,
489                              int quota_fnum,
490                              SMB_NTQUOTA_LIST **pqt_list)
491 {
492         NTSTATUS status;
493         TALLOC_CTX *mem_ctx = NULL;
494         bool first = true;
495
496         if (!cli || !pqt_list) {
497                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
498         }
499
500         *pqt_list = NULL;
501
502         if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
503                 return NT_STATUS_NO_MEMORY;
504         }
505
506         do {
507                 status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
508                                                   pqt_list, first);
509                 first = false;
510         } while (NT_STATUS_IS_OK(status));
511
512         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
513                 status = NT_STATUS_OK;
514         }
515
516         if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
517                 TALLOC_FREE(mem_ctx);
518         }
519
520         return status;
521 }
522
523 NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
524                                SMB_NTQUOTA_STRUCT *pqt)
525 {
526         uint16_t setup[1];
527         uint8_t param[2];
528         uint8_t *rdata=NULL;
529         uint32_t rdata_count=0;
530         NTSTATUS status;
531
532         if (!cli||!pqt) {
533                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
534         }
535
536         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
537                 return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
538         }
539
540         SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
541
542         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
543
544         status = cli_trans(talloc_tos(), cli, SMBtrans2,
545                            NULL, -1, /* name, fid */
546                            0, 0,     /* function, flags */
547                            setup, 1, 0, /* setup */
548                            param, 2, 0, /* param */
549                            NULL, 0, 560, /* data */
550                            NULL,         /* recv_flags2 */
551                            NULL, 0, NULL, /* rsetup */
552                            NULL, 0, NULL, /* rparam */
553                            &rdata, 48, &rdata_count);
554
555         if (!NT_STATUS_IS_OK(status)) {
556                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
557                           nt_errstr(status)));
558                 return status;
559         }
560
561         status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
562
563         TALLOC_FREE(rdata);
564         return status;
565 }
566
567 NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
568                                SMB_NTQUOTA_STRUCT *pqt)
569 {
570         uint16_t setup[1];
571         uint8_t param[4];
572         uint8_t data[48];
573         SMB_NTQUOTA_STRUCT qt;
574         NTSTATUS status;
575         ZERO_STRUCT(qt);
576         memset(data,'\0',48);
577
578         if (!cli||!pqt) {
579                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
580         }
581
582         SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
583
584         SSVAL(param,0,quota_fnum);
585         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
586
587         /* Unknown1 24 NULL bytes*/
588
589         /* Default Soft Quota 8 bytes */
590         SBIG_UINT(data,24,pqt->softlim);
591
592         /* Default Hard Quota 8 bytes */
593         SBIG_UINT(data,32,pqt->hardlim);
594
595         /* Quota flag 2 bytes */
596         SSVAL(data,40,pqt->qflags);
597
598         /* Unknown3 6 NULL bytes */
599
600         status = cli_trans(talloc_tos(), cli, SMBtrans2,
601                            NULL, -1, /* name, fid */
602                            0, 0,     /* function, flags */
603                            setup, 1, 0, /* setup */
604                            param, 4, 0, /* param */
605                            data, 48, 0, /* data */
606                            NULL,         /* recv_flags2 */
607                            NULL, 0, NULL, /* rsetup */
608                            NULL, 0, NULL, /* rparam */
609                            NULL, 0, NULL); /* rdata */
610
611         if (!NT_STATUS_IS_OK(status)) {
612                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
613                           nt_errstr(status)));
614         }
615
616         return status;
617 }