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