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"AS_ENABLED)||(qt->qflags"AS_DENY_DISK))?"On":"Off"); 591 d_printf(" Deny Disk: %s\n",(qt->qflags"AS_DENY_DISK)?"On":"Off"); 592 d_printf(" Log Soft Limit: %s\n",(qt->qflags"AS_LOG_THRESHOLD)?"On":"Off"); 593 d_printf(" Log Hard Limit: %s\n",(qt->qflags"AS_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