cliquota: factor out building of FILE_FS_CONTROL_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 build_fs_quota_buffer(TALLOC_CTX *mem_ctx,
328                                const SMB_NTQUOTA_STRUCT *pqt,
329                                DATA_BLOB *blob,
330                                uint32_t maxlen)
331 {
332         uint8_t *buf;
333
334         if (maxlen > 0 && maxlen < 48) {
335                 return NT_STATUS_BUFFER_TOO_SMALL;
336         }
337
338         *blob = data_blob_talloc_zero(mem_ctx, 48);
339
340         if (!blob->data) {
341                 return NT_STATUS_NO_MEMORY;
342         }
343
344         buf = blob->data;
345
346         /* Unknown1 24 NULL bytes*/
347         SBIG_UINT(buf, 0, (uint64_t)0);
348         SBIG_UINT(buf, 8, (uint64_t)0);
349         SBIG_UINT(buf, 16, (uint64_t)0);
350
351         /* Default Soft Quota 8 bytes */
352         SBIG_UINT(buf, 24, pqt->softlim);
353
354         /* Default Hard Quota 8 bytes */
355         SBIG_UINT(buf, 32, pqt->hardlim);
356
357         /* Quota flag 4 bytes */
358         SIVAL(buf, 40, pqt->qflags);
359
360         /* 4 padding bytes */
361         SIVAL(buf, 44, 0);
362
363         return NT_STATUS_OK;
364 }
365
366 NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum,
367                             SMB_NTQUOTA_STRUCT *pqt)
368 {
369         uint16_t setup[1];
370         uint8_t params[16];
371         unsigned int data_len;
372         uint8_t data[SID_MAX_SIZE+8];
373         uint8_t *rparam, *rdata;
374         uint32_t rparam_count, rdata_count;
375         unsigned int sid_len;
376         unsigned int offset;
377         NTSTATUS status;
378
379         if (!cli||!pqt) {
380                 smb_panic("cli_get_user_quota() called with NULL Pointer!");
381         }
382
383         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
384                 return cli_smb2_get_user_quota(cli, quota_fnum, pqt);
385         }
386
387         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
388
389         SSVAL(params, 0,quota_fnum);
390         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
391         SIVAL(params, 4,0x00000024);
392         SIVAL(params, 8,0x00000000);
393         SIVAL(params,12,0x00000024);
394
395         sid_len = ndr_size_dom_sid(&pqt->sid, 0);
396         data_len = sid_len+8;
397         SIVAL(data, 0, 0x00000000);
398         SIVAL(data, 4, sid_len);
399         sid_linearize(data+8, sid_len, &pqt->sid);
400
401         status = cli_trans(talloc_tos(), cli, SMBnttrans,
402                            NULL, -1, /* name, fid */
403                            NT_TRANSACT_GET_USER_QUOTA, 0,
404                            setup, 1, 0, /* setup */
405                            params, 16, 4, /* params */
406                            data, data_len, 112, /* data */
407                            NULL,                /* recv_flags2 */
408                            NULL, 0, NULL,       /* rsetup */
409                            &rparam, 4, &rparam_count,
410                            &rdata, 8, &rdata_count);
411         if (!NT_STATUS_IS_OK(status)) {
412                 DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n",
413                           nt_errstr(status)));
414                 return status;
415         }
416
417         if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) {
418                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
419                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
420         }
421
422         TALLOC_FREE(rparam);
423         TALLOC_FREE(rdata);
424         return status;
425 }
426
427 NTSTATUS
428 cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl)
429 {
430         uint16_t setup[1];
431         uint8_t params[2];
432         DATA_BLOB data = data_blob_null;
433         NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
434
435         if (!cli || !qtl) {
436                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
437         }
438
439         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
440                 return cli_smb2_set_user_quota(cli, quota_fnum, qtl);
441         }
442
443         status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL);
444         if (!NT_STATUS_IS_OK(status)) {
445                 goto cleanup;
446         }
447
448         SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA);
449
450         SSVAL(params,0,quota_fnum);
451
452         status = cli_trans(talloc_tos(), cli, SMBnttrans,
453                            NULL, -1, /* name, fid */
454                            NT_TRANSACT_SET_USER_QUOTA, 0,
455                            setup, 1, 0, /* setup */
456                            params, 2, 0, /* params */
457                            data.data, data.length, 0, /* data */
458                            NULL,                /* recv_flags2 */
459                            NULL, 0, NULL,       /* rsetup */
460                            NULL, 0, NULL,       /* rparams */
461                            NULL, 0, NULL);      /* rdata */
462
463         if (!NT_STATUS_IS_OK(status)) {
464                 DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n",
465                           nt_errstr(status)));
466         }
467
468 cleanup:
469         data_blob_free(&data);
470         return status;
471 }
472
473 static NTSTATUS cli_list_user_quota_step(struct cli_state *cli,
474                                          TALLOC_CTX *mem_ctx,
475                                          int quota_fnum,
476                                          SMB_NTQUOTA_LIST **pqt_list,
477                                          bool first)
478 {
479         uint16_t setup[1];
480         uint8_t params[16];
481         uint8_t *rparam=NULL, *rdata=NULL;
482         uint32_t rparam_count=0, rdata_count=0;
483         NTSTATUS status;
484         uint16_t op = first ? TRANSACT_GET_USER_QUOTA_LIST_START
485                             : TRANSACT_GET_USER_QUOTA_LIST_CONTINUE;
486
487         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
488                 return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum,
489                                                      pqt_list, first);
490         }
491
492         SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA);
493
494         SSVAL(params, 0,quota_fnum);
495         SSVAL(params, 2, op);
496         SIVAL(params, 4,0x00000000);
497         SIVAL(params, 8,0x00000000);
498         SIVAL(params,12,0x00000000);
499
500         status = cli_trans(talloc_tos(), cli, SMBnttrans,
501                            NULL, -1, /* name, fid */
502                            NT_TRANSACT_GET_USER_QUOTA, 0,
503                            setup, 1, 0, /* setup */
504                            params, 16, 4, /* params */
505                            NULL, 0, 2048, /* data */
506                            NULL,                /* recv_flags2 */
507                            NULL, 0, NULL,       /* rsetup */
508                            &rparam, 0, &rparam_count,
509                            &rdata, 0, &rdata_count);
510
511         /* compat. with smbd + safeguard against
512          * endless loop
513          */
514         if (NT_STATUS_IS_OK(status) && rdata_count == 0) {
515                 status = NT_STATUS_NO_MORE_ENTRIES;
516         }
517
518         if (!NT_STATUS_IS_OK(status)) {
519                 goto cleanup;
520         }
521
522         status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list);
523
524 cleanup:
525         TALLOC_FREE(rparam);
526         TALLOC_FREE(rdata);
527
528         return status;
529 }
530
531 NTSTATUS cli_list_user_quota(struct cli_state *cli,
532                              int quota_fnum,
533                              SMB_NTQUOTA_LIST **pqt_list)
534 {
535         NTSTATUS status;
536         TALLOC_CTX *mem_ctx = NULL;
537         bool first = true;
538
539         if (!cli || !pqt_list) {
540                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
541         }
542
543         *pqt_list = NULL;
544
545         if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) {
546                 return NT_STATUS_NO_MEMORY;
547         }
548
549         do {
550                 status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum,
551                                                   pqt_list, first);
552                 first = false;
553         } while (NT_STATUS_IS_OK(status));
554
555         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
556                 status = NT_STATUS_OK;
557         }
558
559         if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) {
560                 TALLOC_FREE(mem_ctx);
561         }
562
563         return status;
564 }
565
566 NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum,
567                                SMB_NTQUOTA_STRUCT *pqt)
568 {
569         uint16_t setup[1];
570         uint8_t param[2];
571         uint8_t *rdata=NULL;
572         uint32_t rdata_count=0;
573         NTSTATUS status;
574
575         if (!cli||!pqt) {
576                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
577         }
578
579         if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
580                 return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt);
581         }
582
583         SSVAL(setup + 0, 0, TRANSACT2_QFSINFO);
584
585         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
586
587         status = cli_trans(talloc_tos(), cli, SMBtrans2,
588                            NULL, -1, /* name, fid */
589                            0, 0,     /* function, flags */
590                            setup, 1, 0, /* setup */
591                            param, 2, 0, /* param */
592                            NULL, 0, 560, /* data */
593                            NULL,         /* recv_flags2 */
594                            NULL, 0, NULL, /* rsetup */
595                            NULL, 0, NULL, /* rparam */
596                            &rdata, 48, &rdata_count);
597
598         if (!NT_STATUS_IS_OK(status)) {
599                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
600                           nt_errstr(status)));
601                 return status;
602         }
603
604         status = parse_fs_quota_buffer(rdata, rdata_count, pqt);
605
606         TALLOC_FREE(rdata);
607         return status;
608 }
609
610 NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum,
611                                SMB_NTQUOTA_STRUCT *pqt)
612 {
613         uint16_t setup[1];
614         uint8_t param[4];
615         DATA_BLOB data = data_blob_null;
616         NTSTATUS status;
617
618         if (!cli||!pqt) {
619                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
620         }
621
622         status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0);
623         if (!NT_STATUS_IS_OK(status)) {
624                 return status;
625         }
626
627         SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO);
628
629         SSVAL(param,0,quota_fnum);
630         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
631
632         status = cli_trans(talloc_tos(), cli, SMBtrans2,
633                            NULL, -1, /* name, fid */
634                            0, 0,     /* function, flags */
635                            setup, 1, 0, /* setup */
636                            param, 4, 0, /* param */
637                            data.data, data.length, 0, /* data */
638                            NULL,         /* recv_flags2 */
639                            NULL, 0, NULL, /* rsetup */
640                            NULL, 0, NULL, /* rparam */
641                            NULL, 0, NULL); /* rdata */
642
643         if (!NT_STATUS_IS_OK(status)) {
644                 DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n",
645                           nt_errstr(status)));
646         }
647
648         return status;
649 }