Fix bug #7669.
[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
22 bool cli_get_quota_handle(struct cli_state *cli, int *quota_fnum)
23 {
24         *quota_fnum = cli_nt_create_full(cli, FAKE_FILE_NAME_QUOTA_WIN32,
25                  0x00000016, DESIRED_ACCESS_PIPE,
26                  0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE,
27                  FILE_OPEN, 0x00000000, 0x03);
28
29         if (*quota_fnum == (-1)) {
30                 return False;
31         }
32
33         return True;
34 }
35
36 void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list)
37 {
38         if (!qt_list)
39                 return;
40
41         if ((*qt_list)->mem_ctx)
42                 talloc_destroy((*qt_list)->mem_ctx);
43
44         (*qt_list) = NULL;
45
46         return; 
47 }
48
49 static bool parse_user_quota_record(const char *rdata, unsigned int rdata_count, unsigned int *offset, SMB_NTQUOTA_STRUCT *pqt)
50 {
51         int sid_len;
52         SMB_NTQUOTA_STRUCT qt;
53
54         ZERO_STRUCT(qt);
55
56         if (!rdata||!offset||!pqt) {
57                 smb_panic("parse_quota_record: called with NULL POINTER!");
58         }
59
60         if (rdata_count < 40) {
61                 return False;
62         }
63
64         /* offset to next quota record.
65          * 4 bytes IVAL(rdata,0)
66          * unused here...
67          */
68         *offset = IVAL(rdata,0);
69
70         /* sid len */
71         sid_len = IVAL(rdata,4);
72
73         if (rdata_count < 40+sid_len) {
74                 return False;           
75         }
76
77         /* unknown 8 bytes in pdata 
78          * maybe its the change time in NTTIME
79          */
80
81         /* the used space 8 bytes (uint64_t)*/
82         qt.usedspace = (uint64_t)IVAL(rdata,16);
83 #ifdef LARGE_SMB_OFF_T
84         qt.usedspace |= (((uint64_t)IVAL(rdata,20)) << 32);
85 #else /* LARGE_SMB_OFF_T */
86         if ((IVAL(rdata,20) != 0)&&
87                 ((qt.usedspace != 0xFFFFFFFF)||
88                  (IVAL(rdata,20)!=0xFFFFFFFF))) {
89                 /* more than 32 bits? */
90                 return False;
91         }
92 #endif /* LARGE_SMB_OFF_T */
93
94         /* the soft quotas 8 bytes (uint64_t)*/
95         qt.softlim = (uint64_t)IVAL(rdata,24);
96 #ifdef LARGE_SMB_OFF_T
97         qt.softlim |= (((uint64_t)IVAL(rdata,28)) << 32);
98 #else /* LARGE_SMB_OFF_T */
99         if ((IVAL(rdata,28) != 0)&&
100                 ((qt.softlim != 0xFFFFFFFF)||
101                  (IVAL(rdata,28)!=0xFFFFFFFF))) {
102                 /* more than 32 bits? */
103                 return False;
104         }
105 #endif /* LARGE_SMB_OFF_T */
106
107         /* the hard quotas 8 bytes (uint64_t)*/
108         qt.hardlim = (uint64_t)IVAL(rdata,32);
109 #ifdef LARGE_SMB_OFF_T
110         qt.hardlim |= (((uint64_t)IVAL(rdata,36)) << 32);
111 #else /* LARGE_SMB_OFF_T */
112         if ((IVAL(rdata,36) != 0)&&
113                 ((qt.hardlim != 0xFFFFFFFF)||
114                  (IVAL(rdata,36)!=0xFFFFFFFF))) {
115                 /* more than 32 bits? */
116                 return False;
117         }
118 #endif /* LARGE_SMB_OFF_T */
119
120         if (!sid_parse(rdata+40,sid_len,&qt.sid)) {
121                 return false;
122         }
123
124         qt.qtype = SMB_USER_QUOTA_TYPE;
125
126         *pqt = qt;
127
128         return True;
129 }
130
131 bool cli_get_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
132 {
133         bool ret = False;
134         uint16 setup;
135         char params[16];
136         unsigned int data_len;
137         char data[SID_MAX_SIZE+8];
138         char *rparam=NULL, *rdata=NULL;
139         unsigned int rparam_count=0, rdata_count=0;
140         unsigned int sid_len;
141         unsigned int offset;
142
143         if (!cli||!pqt) {
144                 smb_panic("cli_get_user_quota() called with NULL Pointer!");
145         }
146
147         setup = NT_TRANSACT_GET_USER_QUOTA;
148
149         SSVAL(params, 0,quota_fnum);
150         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_FOR_SID);
151         SIVAL(params, 4,0x00000024);
152         SIVAL(params, 8,0x00000000);
153         SIVAL(params,12,0x00000024);
154
155         sid_len = ndr_size_dom_sid(&pqt->sid, NULL, 0);
156         data_len = sid_len+8;
157         SIVAL(data, 0, 0x00000000);
158         SIVAL(data, 4, sid_len);
159         sid_linearize(data+8, sid_len, &pqt->sid);
160
161         if (!cli_send_nt_trans(cli, 
162                                NT_TRANSACT_GET_USER_QUOTA, 
163                                0, 
164                                &setup, 1, 0,
165                                params, 16, 4,
166                                data, data_len, 112)) {
167                 DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
168                 goto cleanup;
169         }
170
171
172         if (!cli_receive_nt_trans(cli,
173                                   &rparam, &rparam_count,
174                                   &rdata, &rdata_count)) {
175                 DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
176                 goto cleanup;
177         }
178
179         if (cli_is_error(cli)) {
180                 ret = False;
181                 goto cleanup;
182         } else {
183                 ret = True;
184         }
185
186         if ((rparam&&rdata)&&(rparam_count>=4&&rdata_count>=8)) {
187                 ret = parse_user_quota_record(rdata, rdata_count, &offset, pqt);
188         } else {
189                 DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n"));
190                 ret = False; 
191         }
192
193  cleanup:
194         SAFE_FREE(rparam);
195         SAFE_FREE(rdata); 
196         return ret;
197 }
198
199 bool cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
200 {
201         bool ret = False;
202         uint16 setup;
203         char params[2];
204         char data[112];
205         char *rparam=NULL, *rdata=NULL;
206         unsigned int rparam_count=0, rdata_count=0;
207         unsigned int sid_len;   
208         memset(data,'\0',112);
209
210         if (!cli||!pqt) {
211                 smb_panic("cli_set_user_quota() called with NULL Pointer!");
212         }
213
214         setup = NT_TRANSACT_SET_USER_QUOTA;
215
216         SSVAL(params,0,quota_fnum);
217
218         sid_len = ndr_size_dom_sid(&pqt->sid, NULL, 0);
219         SIVAL(data,0,0);
220         SIVAL(data,4,sid_len);
221         SBIG_UINT(data, 8,(uint64_t)0);
222         SBIG_UINT(data,16,pqt->usedspace);
223         SBIG_UINT(data,24,pqt->softlim);
224         SBIG_UINT(data,32,pqt->hardlim);
225         sid_linearize(data+40, sid_len, &pqt->sid);
226
227         if (!cli_send_nt_trans(cli, 
228                                NT_TRANSACT_SET_USER_QUOTA, 
229                                0, 
230                                &setup, 1, 0,
231                                params, 2, 0,
232                                data, 112, 0)) {
233                 DEBUG(1,("Failed to send NT_TRANSACT_SET_USER_QUOTA\n"));
234                 goto cleanup;
235         }
236
237
238         if (!cli_receive_nt_trans(cli, 
239                                   &rparam, &rparam_count,
240                                   &rdata, &rdata_count)) {
241                 DEBUG(1,("NT_TRANSACT_SET_USER_QUOTA failed\n"));
242                 goto cleanup;
243         }
244
245         if (cli_is_error(cli)) {
246                 ret = False;
247                 goto cleanup;
248         } else {
249                 ret = True;
250         }
251
252   cleanup:
253         SAFE_FREE(rparam);
254         SAFE_FREE(rdata);
255         return ret;
256 }
257
258 bool cli_list_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST **pqt_list)
259 {
260         bool ret = False;
261         uint16 setup;
262         char params[16];
263         char *rparam=NULL, *rdata=NULL;
264         unsigned int rparam_count=0, rdata_count=0;
265         unsigned int offset;
266         const char *curdata = NULL;
267         unsigned int curdata_count = 0;
268         TALLOC_CTX *mem_ctx = NULL;
269         SMB_NTQUOTA_STRUCT qt;
270         SMB_NTQUOTA_LIST *tmp_list_ent;
271
272         if (!cli||!pqt_list) {
273                 smb_panic("cli_list_user_quota() called with NULL Pointer!");
274         }
275
276         setup = NT_TRANSACT_GET_USER_QUOTA;
277
278         SSVAL(params, 0,quota_fnum);
279         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_START);
280         SIVAL(params, 4,0x00000000);
281         SIVAL(params, 8,0x00000000);
282         SIVAL(params,12,0x00000000);
283
284         if (!cli_send_nt_trans(cli, 
285                                NT_TRANSACT_GET_USER_QUOTA, 
286                                0, 
287                                &setup, 1, 0,
288                                params, 16, 4,
289                                NULL, 0, 2048)) {
290                 DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
291                 goto cleanup;
292         }
293
294
295         if (!cli_receive_nt_trans(cli,
296                                   &rparam, &rparam_count,
297                                   &rdata, &rdata_count)) {
298                 DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
299                 goto cleanup;
300         }
301
302         if (cli_is_error(cli)) {
303                 ret = False;
304                 goto cleanup;
305         } else {
306                 ret = True;
307         }
308
309         if (rdata_count == 0) {
310                 *pqt_list = NULL;
311                 return True;
312         }
313
314         if ((mem_ctx=talloc_init("SMB_USER_QUOTA_LIST"))==NULL) {
315                 DEBUG(0,("talloc_init() failed\n"));
316                 return (-1);
317         }
318
319         offset = 1;
320         for (curdata=rdata,curdata_count=rdata_count;
321                 ((curdata)&&(curdata_count>=8)&&(offset>0));
322                 curdata +=offset,curdata_count -= offset) {
323                 ZERO_STRUCT(qt);
324                 if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
325                         DEBUG(1,("Failed to parse the quota record\n"));
326                         goto cleanup;
327                 }
328
329                 if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
330                         DEBUG(0,("TALLOC_ZERO() failed\n"));
331                         talloc_destroy(mem_ctx);
332                         return (-1);
333                 }
334
335                 if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
336                         DEBUG(0,("TALLOC_ZERO() failed\n"));
337                         talloc_destroy(mem_ctx);
338                         return (-1);
339                 }
340
341                 memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
342                 tmp_list_ent->mem_ctx = mem_ctx;                
343
344                 DLIST_ADD((*pqt_list),tmp_list_ent);
345         }
346
347         SSVAL(params, 2,TRANSACT_GET_USER_QUOTA_LIST_CONTINUE); 
348         while(1) {
349                 if (!cli_send_nt_trans(cli, 
350                                        NT_TRANSACT_GET_USER_QUOTA, 
351                                        0, 
352                                        &setup, 1, 0,
353                                        params, 16, 4,
354                                        NULL, 0, 2048)) {
355                         DEBUG(1,("Failed to send NT_TRANSACT_GET_USER_QUOTA\n"));
356                         goto cleanup;
357                 }
358
359                 SAFE_FREE(rparam);
360                 SAFE_FREE(rdata);
361                 if (!cli_receive_nt_trans(cli,
362                                           &rparam, &rparam_count,
363                                           &rdata, &rdata_count)) {
364                         DEBUG(1,("Failed to recv NT_TRANSACT_GET_USER_QUOTA\n"));
365                         goto cleanup;
366                 }
367
368                 if (cli_is_error(cli)) {
369                         ret = False;
370                         goto cleanup;
371                 } else {
372                         ret = True;
373                 }
374
375                 if (rdata_count == 0) {
376                         break;  
377                 }
378
379                 offset = 1;
380                 for (curdata=rdata,curdata_count=rdata_count;
381                         ((curdata)&&(curdata_count>=8)&&(offset>0));
382                         curdata +=offset,curdata_count -= offset) {
383                         ZERO_STRUCT(qt);
384                         if (!parse_user_quota_record(curdata, curdata_count, &offset, &qt)) {
385                                 DEBUG(1,("Failed to parse the quota record\n"));
386                                 goto cleanup;
387                         }
388
389                         if ((tmp_list_ent=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_LIST))==NULL) {
390                                 DEBUG(0,("TALLOC_ZERO() failed\n"));
391                                 talloc_destroy(mem_ctx);
392                                 goto cleanup;
393                         }
394
395                         if ((tmp_list_ent->quotas=TALLOC_ZERO_P(mem_ctx,SMB_NTQUOTA_STRUCT))==NULL) {
396                                 DEBUG(0,("TALLOC_ZERO() failed\n"));
397                                 talloc_destroy(mem_ctx);
398                                 goto cleanup;
399                         }
400
401                         memcpy(tmp_list_ent->quotas,&qt,sizeof(qt));
402                         tmp_list_ent->mem_ctx = mem_ctx;                
403
404                         DLIST_ADD((*pqt_list),tmp_list_ent);
405                 }
406         }
407
408
409         ret = True;
410  cleanup:
411         SAFE_FREE(rparam);
412         SAFE_FREE(rdata);
413
414         return ret;
415 }
416
417 bool cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
418 {
419         bool ret = False;
420         uint16 setup;
421         char param[2];
422         char *rparam=NULL, *rdata=NULL;
423         unsigned int rparam_count=0, rdata_count=0;
424         SMB_NTQUOTA_STRUCT qt;
425         ZERO_STRUCT(qt);
426
427         if (!cli||!pqt) {
428                 smb_panic("cli_get_fs_quota_info() called with NULL Pointer!");
429         }
430
431         setup = TRANSACT2_QFSINFO;
432
433         SSVAL(param,0,SMB_FS_QUOTA_INFORMATION);
434
435         if (!cli_send_trans(cli, SMBtrans2, 
436                     NULL, 
437                     0, 0,
438                     &setup, 1, 0,
439                     param, 2, 0,
440                     NULL, 0, 560)) {
441                 goto cleanup;
442         }
443
444         if (!cli_receive_trans(cli, SMBtrans2,
445                               &rparam, &rparam_count,
446                               &rdata, &rdata_count)) {
447                 goto cleanup;
448         }
449
450         if (cli_is_error(cli)) {
451                 ret = False;
452                 goto cleanup;
453         } else {
454                 ret = True;
455         }
456
457         if (rdata_count < 48) {
458                 goto cleanup;
459         }
460
461         /* unknown_1 24 NULL bytes in pdata*/
462
463         /* the soft quotas 8 bytes (uint64_t)*/
464         qt.softlim = (uint64_t)IVAL(rdata,24);
465 #ifdef LARGE_SMB_OFF_T
466         qt.softlim |= (((uint64_t)IVAL(rdata,28)) << 32);
467 #else /* LARGE_SMB_OFF_T */
468         if ((IVAL(rdata,28) != 0)&&
469                 ((qt.softlim != 0xFFFFFFFF)||
470                  (IVAL(rdata,28)!=0xFFFFFFFF))) {
471                 /* more than 32 bits? */
472                 goto cleanup;
473         }
474 #endif /* LARGE_SMB_OFF_T */
475
476         /* the hard quotas 8 bytes (uint64_t)*/
477         qt.hardlim = (uint64_t)IVAL(rdata,32);
478 #ifdef LARGE_SMB_OFF_T
479         qt.hardlim |= (((uint64_t)IVAL(rdata,36)) << 32);
480 #else /* LARGE_SMB_OFF_T */
481         if ((IVAL(rdata,36) != 0)&&
482                 ((qt.hardlim != 0xFFFFFFFF)||
483                  (IVAL(rdata,36)!=0xFFFFFFFF))) {
484                 /* more than 32 bits? */
485                 goto cleanup;
486         }
487 #endif /* LARGE_SMB_OFF_T */
488
489         /* quota_flags 2 bytes **/
490         qt.qflags = SVAL(rdata,40);
491
492         qt.qtype = SMB_USER_FS_QUOTA_TYPE;
493
494         *pqt = qt;
495
496         ret = True;
497 cleanup:
498         SAFE_FREE(rparam);
499         SAFE_FREE(rdata);
500
501         return ret;     
502 }
503
504 bool cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_STRUCT *pqt)
505 {
506         bool ret = False;
507         uint16 setup;
508         char param[4];
509         char data[48];
510         char *rparam=NULL, *rdata=NULL;
511         unsigned int rparam_count=0, rdata_count=0;
512         SMB_NTQUOTA_STRUCT qt;
513         ZERO_STRUCT(qt);
514         memset(data,'\0',48);
515
516         if (!cli||!pqt) {
517                 smb_panic("cli_set_fs_quota_info() called with NULL Pointer!");
518         }
519
520         setup = TRANSACT2_SETFSINFO;
521
522         SSVAL(param,0,quota_fnum);
523         SSVAL(param,2,SMB_FS_QUOTA_INFORMATION);
524
525         /* Unknown1 24 NULL bytes*/
526
527         /* Default Soft Quota 8 bytes */
528         SBIG_UINT(data,24,pqt->softlim);
529
530         /* Default Hard Quota 8 bytes */
531         SBIG_UINT(data,32,pqt->hardlim);
532
533         /* Quota flag 2 bytes */
534         SSVAL(data,40,pqt->qflags);
535
536         /* Unknown3 6 NULL bytes */
537
538         if (!cli_send_trans(cli, SMBtrans2, 
539                     NULL, 
540                     0, 0,
541                     &setup, 1, 0,
542                     param, 4, 0,
543                     data, 48, 0)) {
544                 goto cleanup;
545         }
546
547         if (!cli_receive_trans(cli, SMBtrans2,
548                               &rparam, &rparam_count,
549                               &rdata, &rdata_count)) {
550                 goto cleanup;
551         }
552
553         if (cli_is_error(cli)) {
554                 ret = False;
555                 goto cleanup;
556         } else {
557                 ret = True;
558         }
559
560 cleanup:
561         SAFE_FREE(rparam);
562         SAFE_FREE(rdata);
563
564         return ret;     
565 }
566
567 static const char *quota_str_static(uint64_t val, bool special, bool _numeric)
568 {
569         const char *result;
570
571         if (!_numeric&&special&&(val == SMB_NTQUOTAS_NO_LIMIT)) {
572                 return "NO LIMIT";
573         }
574         result = talloc_asprintf(talloc_tos(), "%"PRIu64, val);
575         SMB_ASSERT(result != NULL);
576         return result;
577 }
578
579 void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
580 {
581         TALLOC_CTX *frame = talloc_stackframe();
582
583         if (!qt) {
584                 smb_panic("dump_ntquota() called with NULL pointer");
585         }
586
587         switch (qt->qtype) {
588                 case SMB_USER_FS_QUOTA_TYPE:
589                         {
590                                 d_printf("File System QUOTAS:\n");
591                                 d_printf("Limits:\n");
592                                 d_printf(" Default Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
593                                 d_printf(" Default Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
594                                 d_printf("Quota Flags:\n");
595                                 d_printf(" Quotas Enabled: %s\n",
596                                         ((qt->qflags&QUOTAS_ENABLED)||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
597                                 d_printf(" Deny Disk:      %s\n",(qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
598                                 d_printf(" Log Soft Limit: %s\n",(qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
599                                 d_printf(" Log Hard Limit: %s\n",(qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
600                         }
601                         break;
602                 case SMB_USER_QUOTA_TYPE:
603                         {
604                                 fstring username_str = {0};
605
606                                 if (_sidtostring) {
607                                         _sidtostring(username_str,&qt->sid,_numeric);
608                                 } else {
609                                         sid_to_fstring(username_str, &qt->sid);
610                                 }
611
612                                 if (_verbose) { 
613                                         d_printf("Quotas for User: %s\n",username_str);
614                                         d_printf("Used Space: %15s\n",quota_str_static(qt->usedspace,False,_numeric));
615                                         d_printf("Soft Limit: %15s\n",quota_str_static(qt->softlim,True,_numeric));
616                                         d_printf("Hard Limit: %15s\n",quota_str_static(qt->hardlim,True,_numeric));
617                                 } else {
618                                         d_printf("%-30s: ",username_str);
619                                         d_printf("%15s/",quota_str_static(qt->usedspace,False,_numeric));
620                                         d_printf("%15s/",quota_str_static(qt->softlim,True,_numeric));
621                                         d_printf("%15s\n",quota_str_static(qt->hardlim,True,_numeric));
622                                 }
623                         }
624                         break;
625                 default:
626                         d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
627         }
628         TALLOC_FREE(frame);
629         return;
630 }
631
632 void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, bool _verbose, bool _numeric, void (*_sidtostring)(fstring str, DOM_SID *sid, bool _numeric))
633 {
634         SMB_NTQUOTA_LIST *cur;
635
636         for (cur = *qtl;cur;cur = cur->next) {
637                 if (cur->quotas)
638                         dump_ntquota(cur->quotas,_verbose,_numeric,_sidtostring);
639         }       
640 }