tests: Check symlinks are readable as reparse points
[samba.git] / source3 / smbd / smb2_nttrans.c
blob19e78ab1394758004c857039df090f8753e8399d
1 /*
2 Unix SMB/CIFS implementation.
3 SMB NT transaction handling
4 Copyright (C) Jeremy Allison 1994-2007
5 Copyright (C) Stefan (metze) Metzmacher 2003
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "smbd/globals.h"
25 #include "fake_file.h"
26 #include "../libcli/security/security.h"
27 #include "../librpc/gen_ndr/ndr_security.h"
28 #include "passdb/lookup_sid.h"
29 #include "auth.h"
30 #include "smbprofile.h"
31 #include "libsmb/libsmb.h"
32 #include "lib/util_ea.h"
33 #include "librpc/gen_ndr/ndr_quota.h"
34 #include "librpc/gen_ndr/ndr_security.h"
36 extern const struct generic_mapping file_generic_mapping;
38 /*********************************************************************
39 Windows seems to do canonicalization of inheritance bits. Do the
40 same.
41 *********************************************************************/
43 static void canonicalize_inheritance_bits(struct files_struct *fsp,
44 struct security_descriptor *psd)
46 bool set_auto_inherited = false;
49 * We need to filter out the
50 * SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ
51 * bits. If both are set we store SEC_DESC_DACL_AUTO_INHERITED
52 * as this alters whether SEC_ACE_FLAG_INHERITED_ACE is set
53 * when an ACE is inherited. Otherwise we zero these bits out.
54 * See:
56 * http://social.msdn.microsoft.com/Forums/eu/os_fileservices/thread/11f77b68-731e-407d-b1b3-064750716531
58 * for details.
61 if (!lp_acl_flag_inherited_canonicalization(SNUM(fsp->conn))) {
62 psd->type &= ~SEC_DESC_DACL_AUTO_INHERIT_REQ;
63 return;
66 if ((psd->type & (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ))
67 == (SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ)) {
68 set_auto_inherited = true;
71 psd->type &= ~(SEC_DESC_DACL_AUTO_INHERITED|SEC_DESC_DACL_AUTO_INHERIT_REQ);
72 if (set_auto_inherited) {
73 psd->type |= SEC_DESC_DACL_AUTO_INHERITED;
77 /****************************************************************************
78 Internal fn to set security descriptors.
79 ****************************************************************************/
81 NTSTATUS set_sd(files_struct *fsp, struct security_descriptor *psd,
82 uint32_t security_info_sent)
84 files_struct *sd_fsp = NULL;
85 NTSTATUS status;
86 bool refuse;
88 if (!CAN_WRITE(fsp->conn)) {
89 return NT_STATUS_ACCESS_DENIED;
92 if (!lp_nt_acl_support(SNUM(fsp->conn))) {
93 return NT_STATUS_OK;
96 refuse = refuse_symlink_fsp(fsp);
97 if (refuse) {
98 DBG_DEBUG("ACL set on symlink %s denied.\n",
99 fsp_str_dbg(fsp));
100 return NT_STATUS_ACCESS_DENIED;
103 if (psd->owner_sid == NULL) {
104 security_info_sent &= ~SECINFO_OWNER;
106 if (psd->group_sid == NULL) {
107 security_info_sent &= ~SECINFO_GROUP;
110 /* Ensure we have at least one thing set. */
111 if ((security_info_sent & (SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL|SECINFO_SACL)) == 0) {
112 /* Just like W2K3 */
113 return NT_STATUS_OK;
116 /* Ensure we have the rights to do this. */
117 if (security_info_sent & SECINFO_OWNER) {
118 status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
119 if (!NT_STATUS_IS_OK(status)) {
120 return status;
124 if (security_info_sent & SECINFO_GROUP) {
125 status = check_any_access_fsp(fsp, SEC_STD_WRITE_OWNER);
126 if (!NT_STATUS_IS_OK(status)) {
127 return status;
131 if (security_info_sent & SECINFO_DACL) {
132 status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC);
133 if (!NT_STATUS_IS_OK(status)) {
134 return status;
136 /* Convert all the generic bits. */
137 if (psd->dacl) {
138 security_acl_map_generic(psd->dacl, &file_generic_mapping);
142 if (security_info_sent & SECINFO_SACL) {
143 status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY);
144 if (!NT_STATUS_IS_OK(status)) {
145 return status;
148 * Setting a SACL also requires WRITE_DAC.
149 * See the smbtorture3 SMB2-SACL test.
151 status = check_any_access_fsp(fsp, SEC_STD_WRITE_DAC);
152 if (!NT_STATUS_IS_OK(status)) {
153 return status;
155 /* Convert all the generic bits. */
156 if (psd->sacl) {
157 security_acl_map_generic(psd->sacl, &file_generic_mapping);
161 canonicalize_inheritance_bits(fsp, psd);
163 if (DEBUGLEVEL >= 10) {
164 DEBUG(10,("set_sd for file %s\n", fsp_str_dbg(fsp)));
165 NDR_PRINT_DEBUG(security_descriptor, psd);
168 sd_fsp = metadata_fsp(fsp);
169 status = SMB_VFS_FSET_NT_ACL(sd_fsp, security_info_sent, psd);
171 TALLOC_FREE(psd);
173 return status;
176 static bool check_smb2_posix_chmod_ace(const struct files_struct *fsp,
177 uint32_t security_info_sent,
178 struct security_descriptor *psd,
179 mode_t *pmode)
181 struct security_ace *ace = NULL;
182 int cmp;
185 * This must be an ACL with one ACE containing an
186 * MS NFS style mode entry coming in on a POSIX
187 * handle over SMB2+.
189 if (!conn_using_smb2(fsp->conn->sconn)) {
190 return false;
193 if (!fsp->fsp_flags.posix_open) {
194 return false;
197 if (!(security_info_sent & SECINFO_DACL)) {
198 return false;
201 if (psd->dacl == NULL) {
202 return false;
205 if (psd->dacl->num_aces != 1) {
206 return false;
208 ace = &psd->dacl->aces[0];
210 if (ace->trustee.num_auths != 3) {
211 return false;
214 cmp = dom_sid_compare_domain(&global_sid_Unix_NFS_Mode, &ace->trustee);
215 if (cmp != 0) {
216 return false;
219 *pmode = (mode_t)ace->trustee.sub_auths[2];
220 *pmode &= (S_IRWXU | S_IRWXG | S_IRWXO);
222 return true;
225 /****************************************************************************
226 Internal fn to set security descriptors from a data blob.
227 ****************************************************************************/
229 NTSTATUS set_sd_blob(files_struct *fsp, uint8_t *data, uint32_t sd_len,
230 uint32_t security_info_sent)
232 struct security_descriptor *psd = NULL;
233 NTSTATUS status;
234 bool do_chmod = false;
235 mode_t smb2_posix_mode = 0;
236 int ret;
238 if (sd_len == 0) {
239 return NT_STATUS_INVALID_PARAMETER;
242 status = unmarshall_sec_desc(talloc_tos(), data, sd_len, &psd);
244 if (!NT_STATUS_IS_OK(status)) {
245 return status;
248 do_chmod = check_smb2_posix_chmod_ace(fsp,
249 security_info_sent,
250 psd,
251 &smb2_posix_mode);
252 if (!do_chmod) {
253 return set_sd(fsp, psd, security_info_sent);
256 TALLOC_FREE(psd);
258 ret = SMB_VFS_FCHMOD(fsp, smb2_posix_mode);
259 if (ret != 0) {
260 status = map_nt_error_from_unix(errno);
261 DBG_ERR("smb2_posix_chmod [%s] [%04o] failed: %s\n",
262 fsp_str_dbg(fsp),
263 (unsigned)smb2_posix_mode,
264 nt_errstr(status));
265 return status;
268 return NT_STATUS_OK;
271 /****************************************************************************
272 Copy a file.
273 ****************************************************************************/
275 NTSTATUS copy_internals(TALLOC_CTX *ctx,
276 connection_struct *conn,
277 struct smb_request *req,
278 struct files_struct *src_dirfsp,
279 struct smb_filename *smb_fname_src,
280 struct files_struct *dst_dirfsp,
281 struct smb_filename *smb_fname_dst,
282 uint32_t attrs)
284 files_struct *fsp1,*fsp2;
285 uint32_t fattr;
286 int info;
287 off_t ret=-1;
288 NTSTATUS status = NT_STATUS_OK;
289 struct smb_filename *parent = NULL;
290 struct smb_filename *pathref = NULL;
292 if (!CAN_WRITE(conn)) {
293 status = NT_STATUS_MEDIA_WRITE_PROTECTED;
294 goto out;
297 /* Source must already exist. */
298 if (!VALID_STAT(smb_fname_src->st)) {
299 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
300 goto out;
303 /* Ensure attributes match. */
304 fattr = fdos_mode(smb_fname_src->fsp);
305 if ((fattr & ~attrs) & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) {
306 status = NT_STATUS_NO_SUCH_FILE;
307 goto out;
310 /* Disallow if dst file already exists. */
311 if (VALID_STAT(smb_fname_dst->st)) {
312 status = NT_STATUS_OBJECT_NAME_COLLISION;
313 goto out;
316 /* No links from a directory. */
317 if (S_ISDIR(smb_fname_src->st.st_ex_mode)) {
318 status = NT_STATUS_FILE_IS_A_DIRECTORY;
319 goto out;
322 DBG_DEBUG("doing file copy %s to %s\n",
323 smb_fname_str_dbg(smb_fname_src),
324 smb_fname_str_dbg(smb_fname_dst));
326 status = SMB_VFS_CREATE_FILE(
327 conn, /* conn */
328 req, /* req */
329 src_dirfsp, /* dirfsp */
330 smb_fname_src, /* fname */
331 FILE_READ_DATA|FILE_READ_ATTRIBUTES|
332 FILE_READ_EA, /* access_mask */
333 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
334 FILE_SHARE_DELETE),
335 FILE_OPEN, /* create_disposition*/
336 0, /* create_options */
337 FILE_ATTRIBUTE_NORMAL, /* file_attributes */
338 NO_OPLOCK, /* oplock_request */
339 NULL, /* lease */
340 0, /* allocation_size */
341 0, /* private_flags */
342 NULL, /* sd */
343 NULL, /* ea_list */
344 &fsp1, /* result */
345 &info, /* pinfo */
346 NULL, NULL); /* create context */
348 if (!NT_STATUS_IS_OK(status)) {
349 goto out;
352 status = SMB_VFS_CREATE_FILE(
353 conn, /* conn */
354 req, /* req */
355 dst_dirfsp, /* dirfsp */
356 smb_fname_dst, /* fname */
357 FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|
358 FILE_WRITE_EA, /* access_mask */
359 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
360 FILE_SHARE_DELETE),
361 FILE_CREATE, /* create_disposition*/
362 0, /* create_options */
363 fattr, /* file_attributes */
364 NO_OPLOCK, /* oplock_request */
365 NULL, /* lease */
366 0, /* allocation_size */
367 0, /* private_flags */
368 NULL, /* sd */
369 NULL, /* ea_list */
370 &fsp2, /* result */
371 &info, /* pinfo */
372 NULL, NULL); /* create context */
374 if (!NT_STATUS_IS_OK(status)) {
375 close_file_free(NULL, &fsp1, ERROR_CLOSE);
376 goto out;
379 if (smb_fname_src->st.st_ex_size) {
380 ret = vfs_transfer_file(fsp1, fsp2, smb_fname_src->st.st_ex_size);
384 * As we are opening fsp1 read-only we only expect
385 * an error on close on fsp2 if we are out of space.
386 * Thus we don't look at the error return from the
387 * close of fsp1.
389 close_file_free(NULL, &fsp1, NORMAL_CLOSE);
391 /* Ensure the modtime is set correctly on the destination file. */
392 set_close_write_time(fsp2, smb_fname_src->st.st_ex_mtime);
394 status = close_file_free(NULL, &fsp2, NORMAL_CLOSE);
395 if (!NT_STATUS_IS_OK(status)) {
396 DBG_WARNING("close_file_free() failed: %s\n",
397 nt_errstr(status));
399 * We can't do much but leak the fsp
401 goto out;
404 /* Grrr. We have to do this as open_file_ntcreate adds FILE_ATTRIBUTE_ARCHIVE when it
405 creates the file. This isn't the correct thing to do in the copy
406 case. JRA */
408 status = SMB_VFS_PARENT_PATHNAME(conn,
409 talloc_tos(),
410 smb_fname_dst,
411 &parent,
412 NULL);
413 if (!NT_STATUS_IS_OK(status)) {
414 goto out;
416 if (smb_fname_dst->fsp == NULL) {
417 status = synthetic_pathref(parent,
418 conn->cwd_fsp,
419 smb_fname_dst->base_name,
420 smb_fname_dst->stream_name,
421 NULL,
422 smb_fname_dst->twrp,
423 smb_fname_dst->flags,
424 &pathref);
426 /* should we handle NT_STATUS_OBJECT_NAME_NOT_FOUND specially here ???? */
427 if (!NT_STATUS_IS_OK(status)) {
428 TALLOC_FREE(parent);
429 goto out;
431 file_set_dosmode(conn, pathref, fattr, parent, false);
432 smb_fname_dst->st.st_ex_mode = pathref->st.st_ex_mode;
433 } else {
434 file_set_dosmode(conn, smb_fname_dst, fattr, parent, false);
436 TALLOC_FREE(parent);
438 if (ret < (off_t)smb_fname_src->st.st_ex_size) {
439 status = NT_STATUS_DISK_FULL;
440 goto out;
442 out:
443 if (!NT_STATUS_IS_OK(status)) {
444 DBG_NOTICE("Error %s copy file %s to %s\n",
445 nt_errstr(status),
446 smb_fname_str_dbg(smb_fname_src),
447 smb_fname_str_dbg(smb_fname_dst));
450 return status;
453 /******************************************************************************
454 Fake up a completely empty SD.
455 *******************************************************************************/
457 static NTSTATUS get_null_nt_acl(TALLOC_CTX *mem_ctx, struct security_descriptor **ppsd)
459 size_t sd_size;
461 *ppsd = make_standard_sec_desc( mem_ctx, &global_sid_World, &global_sid_World, NULL, &sd_size);
462 if(!*ppsd) {
463 DBG_ERR("Unable to malloc space for security descriptor.\n");
464 return NT_STATUS_NO_MEMORY;
467 return NT_STATUS_OK;
470 /****************************************************************************
471 Get a security descriptor from the file system, normalize for components
472 requested.
473 ****************************************************************************/
475 static NTSTATUS smbd_fetch_security_desc(connection_struct *conn,
476 TALLOC_CTX *mem_ctx,
477 files_struct *fsp,
478 uint32_t security_info_wanted,
479 struct security_descriptor **ppsd)
481 NTSTATUS status;
482 struct security_descriptor *psd = NULL;
483 bool need_to_read_sd = false;
484 bool refuse;
487 * Get the permissions to return.
490 if (security_info_wanted & SECINFO_SACL) {
491 status = check_any_access_fsp(fsp, SEC_FLAG_SYSTEM_SECURITY);
492 if (!NT_STATUS_IS_OK(status)) {
493 DBG_DEBUG("Access to SACL denied.\n");
494 return status;
498 if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|SECINFO_GROUP)) {
499 status = check_any_access_fsp(fsp, SEC_STD_READ_CONTROL);
500 if (!NT_STATUS_IS_OK(status)) {
501 DBG_DEBUG("Access to DACL, OWNER, or GROUP denied.\n");
502 return status;
506 refuse = refuse_symlink_fsp(fsp);
507 if (refuse) {
508 DBG_DEBUG("ACL get on symlink %s denied.\n",
509 fsp_str_dbg(fsp));
510 return NT_STATUS_ACCESS_DENIED;
513 if (security_info_wanted & (SECINFO_DACL|SECINFO_OWNER|
514 SECINFO_GROUP|SECINFO_SACL)) {
515 /* Don't return SECINFO_LABEL if anything else was
516 requested. See bug #8458. */
517 security_info_wanted &= ~SECINFO_LABEL;
520 * Only query the file system SD if the caller asks
521 * for any bits. This allows a caller to open without
522 * READ_CONTROL but still issue a query sd. See
523 * smb2.sdread test.
525 need_to_read_sd = true;
528 if (lp_nt_acl_support(SNUM(conn)) &&
529 ((security_info_wanted & SECINFO_LABEL) == 0) &&
530 need_to_read_sd)
532 files_struct *sd_fsp = metadata_fsp(fsp);
533 status = SMB_VFS_FGET_NT_ACL(
534 sd_fsp, security_info_wanted, mem_ctx, &psd);
535 } else {
536 status = get_null_nt_acl(mem_ctx, &psd);
539 if (!NT_STATUS_IS_OK(status)) {
540 return status;
543 if (!(security_info_wanted & SECINFO_OWNER)) {
544 psd->owner_sid = NULL;
546 if (!(security_info_wanted & SECINFO_GROUP)) {
547 psd->group_sid = NULL;
549 if (!(security_info_wanted & SECINFO_DACL)) {
550 psd->type &= ~SEC_DESC_DACL_PRESENT;
551 psd->dacl = NULL;
553 if (!(security_info_wanted & SECINFO_SACL)) {
554 psd->type &= ~SEC_DESC_SACL_PRESENT;
555 psd->sacl = NULL;
558 /* If the SACL/DACL is NULL, but was requested, we mark that it is
559 * present in the reply to match Windows behavior */
560 if (psd->sacl == NULL &&
561 security_info_wanted & SECINFO_SACL)
562 psd->type |= SEC_DESC_SACL_PRESENT;
563 if (psd->dacl == NULL &&
564 security_info_wanted & SECINFO_DACL)
565 psd->type |= SEC_DESC_DACL_PRESENT;
567 if (security_info_wanted & SECINFO_LABEL) {
568 /* Like W2K3 return a null object. */
569 psd->owner_sid = NULL;
570 psd->group_sid = NULL;
571 psd->dacl = NULL;
572 psd->sacl = NULL;
573 psd->type &= ~(SEC_DESC_DACL_PRESENT|SEC_DESC_SACL_PRESENT);
576 *ppsd = psd;
577 return NT_STATUS_OK;
580 /****************************************************************************
581 Write a security descriptor into marshalled format.
582 ****************************************************************************/
584 static NTSTATUS smbd_marshall_security_desc(TALLOC_CTX *mem_ctx,
585 files_struct *fsp,
586 struct security_descriptor *psd,
587 uint32_t max_data_count,
588 uint8_t **ppmarshalled_sd,
589 size_t *psd_size)
591 *psd_size = ndr_size_security_descriptor(psd, 0);
593 DBG_NOTICE("sd_size = %zu.\n", *psd_size);
595 if (DEBUGLEVEL >= 10) {
596 DBG_DEBUG("security desc for file %s\n",
597 fsp_str_dbg(fsp));
598 NDR_PRINT_DEBUG(security_descriptor, psd);
601 if (max_data_count < *psd_size) {
602 return NT_STATUS_BUFFER_TOO_SMALL;
605 return marshall_sec_desc(mem_ctx,
606 psd,
607 ppmarshalled_sd,
608 psd_size);
611 /****************************************************************************
612 Reply to query a security descriptor.
613 Callable from SMB1 and SMB2.
614 If it returns NT_STATUS_BUFFER_TOO_SMALL, psd_size is initialized with
615 the required size.
616 ****************************************************************************/
618 NTSTATUS smbd_do_query_security_desc(connection_struct *conn,
619 TALLOC_CTX *mem_ctx,
620 files_struct *fsp,
621 uint32_t security_info_wanted,
622 uint32_t max_data_count,
623 uint8_t **ppmarshalled_sd,
624 size_t *psd_size)
626 NTSTATUS status;
627 struct security_descriptor *psd = NULL;
630 * Get the permissions to return.
633 status = smbd_fetch_security_desc(conn,
634 mem_ctx,
635 fsp,
636 security_info_wanted,
637 &psd);
638 if (!NT_STATUS_IS_OK(status)) {
639 return status;
642 status = smbd_marshall_security_desc(mem_ctx,
643 fsp,
644 psd,
645 max_data_count,
646 ppmarshalled_sd,
647 psd_size);
648 TALLOC_FREE(psd);
649 return status;
652 #ifdef HAVE_SYS_QUOTAS
653 static enum ndr_err_code fill_qtlist_from_sids(TALLOC_CTX *mem_ctx,
654 struct files_struct *fsp,
655 SMB_NTQUOTA_HANDLE *qt_handle,
656 struct dom_sid *sids,
657 uint32_t elems)
659 uint32_t i;
660 TALLOC_CTX *list_ctx = NULL;
662 list_ctx = talloc_init("quota_sid_list");
664 if (list_ctx == NULL) {
665 DBG_ERR("failed to allocate\n");
666 return NDR_ERR_ALLOC;
669 if (qt_handle->quota_list!=NULL) {
670 free_ntquota_list(&(qt_handle->quota_list));
672 for (i = 0; i < elems; i++) {
673 SMB_NTQUOTA_STRUCT qt;
674 SMB_NTQUOTA_LIST *list_item;
675 bool ok;
677 if (!NT_STATUS_IS_OK(vfs_get_ntquota(fsp,
678 SMB_USER_QUOTA_TYPE,
679 &sids[i], &qt))) {
680 /* non fatal error, return empty item in result */
681 ZERO_STRUCT(qt);
682 continue;
686 list_item = talloc_zero(list_ctx, SMB_NTQUOTA_LIST);
687 if (list_item == NULL) {
688 DBG_ERR("failed to allocate\n");
689 return NDR_ERR_ALLOC;
692 ok = sid_to_uid(&sids[i], &list_item->uid);
693 if (!ok) {
694 struct dom_sid_buf buf;
695 DBG_WARNING("Could not convert SID %s to uid\n",
696 dom_sid_str_buf(&sids[i], &buf));
697 /* No idea what to return here... */
698 return NDR_ERR_INVALID_POINTER;
701 list_item->quotas = talloc_zero(list_item, SMB_NTQUOTA_STRUCT);
702 if (list_item->quotas == NULL) {
703 DBG_ERR("failed to allocate\n");
704 return NDR_ERR_ALLOC;
707 *list_item->quotas = qt;
708 list_item->mem_ctx = list_ctx;
709 DLIST_ADD(qt_handle->quota_list, list_item);
711 qt_handle->tmp_list = qt_handle->quota_list;
712 return NDR_ERR_SUCCESS;
715 static enum ndr_err_code extract_sids_from_buf(TALLOC_CTX *mem_ctx,
716 uint32_t sidlistlength,
717 DATA_BLOB *sid_buf,
718 struct dom_sid **sids,
719 uint32_t *num)
721 DATA_BLOB blob;
722 uint32_t i = 0;
723 enum ndr_err_code err;
725 struct sid_list_elem {
726 struct sid_list_elem *prev, *next;
727 struct dom_sid sid;
730 struct sid_list_elem *sid_list = NULL;
731 struct sid_list_elem *iter = NULL;
732 TALLOC_CTX *list_ctx = talloc_init("sid_list");
733 if (!list_ctx) {
734 DBG_ERR("OOM\n");
735 err = NDR_ERR_ALLOC;
736 goto done;
739 *num = 0;
740 *sids = NULL;
742 if (sidlistlength) {
743 uint32_t offset = 0;
744 struct ndr_pull *ndr_pull = NULL;
746 if (sidlistlength > sid_buf->length) {
747 DBG_ERR("sid_list_length 0x%x exceeds "
748 "available bytes %zx\n",
749 sidlistlength,
750 sid_buf->length);
751 err = NDR_ERR_OFFSET;
752 goto done;
754 while (true) {
755 struct file_get_quota_info info;
756 struct sid_list_elem *item = NULL;
757 uint32_t new_offset = 0;
758 blob.data = sid_buf->data + offset;
759 blob.length = sidlistlength - offset;
760 ndr_pull = ndr_pull_init_blob(&blob, list_ctx);
761 if (!ndr_pull) {
762 DBG_ERR("OOM\n");
763 err = NDR_ERR_ALLOC;
764 goto done;
766 err = ndr_pull_file_get_quota_info(ndr_pull,
767 NDR_SCALARS | NDR_BUFFERS, &info);
768 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
769 DBG_ERR("Failed to pull file_get_quota_info "
770 "from sidlist buffer\n");
771 goto done;
773 item = talloc_zero(list_ctx, struct sid_list_elem);
774 if (!item) {
775 DBG_ERR("OOM\n");
776 err = NDR_ERR_ALLOC;
777 goto done;
779 item->sid = info.sid;
780 DLIST_ADD(sid_list, item);
781 i++;
782 if (i == UINT32_MAX) {
783 DBG_ERR("Integer overflow\n");
784 err = NDR_ERR_ARRAY_SIZE;
785 goto done;
787 new_offset = info.next_entry_offset;
789 /* if new_offset == 0 no more sid(s) to read. */
790 if (new_offset == 0) {
791 break;
794 /* Integer wrap? */
795 if ((offset + new_offset) < offset) {
796 DBG_ERR("Integer wrap while adding "
797 "new_offset 0x%x to current "
798 "buffer offset 0x%x\n",
799 new_offset, offset);
800 err = NDR_ERR_OFFSET;
801 goto done;
804 offset += new_offset;
806 /* check if new offset is outside buffer boundary. */
807 if (offset >= sidlistlength) {
808 DBG_ERR("bufsize 0x%x exceeded by "
809 "new offset 0x%x)\n",
810 sidlistlength,
811 offset);
812 err = NDR_ERR_OFFSET;
813 goto done;
816 *sids = talloc_zero_array(mem_ctx, struct dom_sid, i);
817 if (*sids == NULL) {
818 DBG_ERR("OOM\n");
819 err = NDR_ERR_ALLOC;
820 goto done;
823 *num = i;
825 for (iter = sid_list, i = 0; iter; iter = iter->next, i++) {
826 struct dom_sid_buf buf;
827 (*sids)[i] = iter->sid;
828 DBG_DEBUG("quota SID[%u] %s\n",
829 (unsigned int)i,
830 dom_sid_str_buf(&iter->sid, &buf));
833 err = NDR_ERR_SUCCESS;
834 done:
835 TALLOC_FREE(list_ctx);
836 return err;
839 NTSTATUS smbd_do_query_getinfo_quota(TALLOC_CTX *mem_ctx,
840 files_struct *fsp,
841 bool restart_scan,
842 bool return_single,
843 uint32_t sid_list_length,
844 DATA_BLOB *sid_buf,
845 uint32_t max_data_count,
846 uint8_t **p_data,
847 uint32_t *p_data_size)
849 NTSTATUS status;
850 SMB_NTQUOTA_HANDLE *qt_handle = NULL;
851 SMB_NTQUOTA_LIST *qt_list = NULL;
852 DATA_BLOB blob = data_blob_null;
853 enum ndr_err_code err;
855 qt_handle =
856 (SMB_NTQUOTA_HANDLE *)fsp->fake_file_handle->private_data;
858 if (sid_list_length ) {
859 struct dom_sid *sids;
860 uint32_t elems = 0;
862 * error check pulled offsets and lengths for wrap and
863 * exceeding available bytes.
865 if (sid_list_length > sid_buf->length) {
866 DBG_ERR("sid_list_length 0x%x exceeds "
867 "available bytes %zx\n",
868 sid_list_length,
869 sid_buf->length);
870 return NT_STATUS_INVALID_PARAMETER;
873 err = extract_sids_from_buf(mem_ctx, sid_list_length,
874 sid_buf, &sids, &elems);
875 if (!NDR_ERR_CODE_IS_SUCCESS(err) || elems == 0) {
876 return NT_STATUS_INVALID_PARAMETER;
878 err = fill_qtlist_from_sids(mem_ctx,
879 fsp,
880 qt_handle,
881 sids,
882 elems);
883 if (!NDR_ERR_CODE_IS_SUCCESS(err)) {
884 return NT_STATUS_INVALID_PARAMETER;
886 } else if (restart_scan) {
887 if (vfs_get_user_ntquota_list(fsp,
888 &(qt_handle->quota_list))!=0) {
889 return NT_STATUS_INTERNAL_ERROR;
891 } else {
892 if (qt_handle->quota_list!=NULL &&
893 qt_handle->tmp_list==NULL) {
894 free_ntquota_list(&(qt_handle->quota_list));
898 if (restart_scan !=0 ) {
899 qt_list = qt_handle->quota_list;
900 } else {
901 qt_list = qt_handle->tmp_list;
903 status = fill_quota_buffer(mem_ctx, qt_list,
904 return_single != 0,
905 max_data_count,
906 &blob,
907 &qt_handle->tmp_list);
908 if (!NT_STATUS_IS_OK(status)) {
909 return status;
911 if (blob.length > max_data_count) {
912 return NT_STATUS_BUFFER_TOO_SMALL;
915 *p_data = blob.data;
916 *p_data_size = blob.length;
917 return NT_STATUS_OK;
919 #endif /* HAVE_SYS_QUOTAS */