]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smb2_ioctl: fix truncated FSCTL_QUERY_ALLOCATED_RANGES responses
authorDavid Disseldorp <ddiss@samba.org>
Fri, 23 Aug 2024 12:55:58 +0000 (12:55 +0000)
committerJule Anger <janger@samba.org>
Mon, 2 Sep 2024 10:02:09 +0000 (10:02 +0000)
As per MS-FSA 2.1.5.10.22 FSCTL_QUERY_ALLOCATED_RANGES, if response
range entries exceed in_max_output, then we should respond with
STATUS_BUFFER_OVERFLOW and a truncated output buffer.

Bug: https://bugzilla.samba.org/show_bug.cgi?id=15699

Reported-by: David Howells <dhowells@redhat.com>
Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Noel Power <npower@samba.org>
Autobuild-User(master): David Disseldorp <ddiss@samba.org>
Autobuild-Date(master): Wed Aug 28 08:54:11 UTC 2024 on atb-devel-224

(cherry picked from commit 5e278a52646a48e3671270e5b57ec5b852f9fb4b)

Autobuild-User(v4-19-test): Jule Anger <janger@samba.org>
Autobuild-Date(v4-19-test): Mon Sep  2 10:02:09 UTC 2024 on atb-devel-224

selftest/knownfail
source3/smbd/smb2_ioctl.c
source3/smbd/smb2_ioctl_filesys.c
source4/libcli/smb2/ioctl.c

index 5f3d4032c7007e29aeb68fd5b961767f3428bb0c..4e34effbbd14af41a2d175d97d265b55ed152acd 100644 (file)
 ^samba3.smb2.lease.statopen3
 ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
 ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
-^samba4.smb2.ioctl.sparse_qar_truncated # bug 15699
 ^samba3.raw.session.*reauth2 # maybe fix this?
 ^samba3.rpc.lsa.secrets.seal # This gives NT_STATUS_LOCAL_USER_SESSION_KEY
 ^samba3.rpc.samr.passwords.badpwdcount.samr.badPwdCount\(nt4_dc\) # We fail this test currently
index 2ca53e337883d358890c001884f1c4c375786d98..bd892ad88935125e39c10a2846c39c563efa8a72 100644 (file)
@@ -268,7 +268,8 @@ static bool smbd_smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
        if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)
         && ((ctl_code == FSCTL_PIPE_TRANSCEIVE)
          || (ctl_code == FSCTL_PIPE_PEEK)
-         || (ctl_code == FSCTL_DFS_GET_REFERRALS))) {
+         || (ctl_code == FSCTL_DFS_GET_REFERRALS)
+         || (ctl_code == FSCTL_QUERY_ALLOCATED_RANGES))) {
                return false;
        }
 
@@ -344,6 +345,7 @@ static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq)
                 * in:
                 * - fsctl_dfs_get_refers()
                 * - smbd_smb2_ioctl_pipe_read_done()
+                * - fsctl_qar()
                 */
                status = NT_STATUS_BUFFER_TOO_SMALL;
        }
index 6cc53d4828ed05c06963c5e98507ec1ed276fed1..1a8d1c2affa057f65b3056395815a8b3f1ccc44b 100644 (file)
@@ -3,7 +3,7 @@
    Core SMB2 server
 
    Copyright (C) Stefan Metzmacher 2009
-   Copyright (C) David Disseldorp 2013-2015
+   Copyright (C) David Disseldorp 2013-2024
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -538,6 +538,7 @@ static NTSTATUS fsctl_qar_seek_fill(TALLOC_CTX *mem_ctx,
                                    struct files_struct *fsp,
                                    off_t curr_off,
                                    off_t max_off,
+                                   size_t in_max_output,
                                    DATA_BLOB *qar_array_blob)
 {
        NTSTATUS status = NT_STATUS_NOT_SUPPORTED;
@@ -578,6 +579,17 @@ static NTSTATUS fsctl_qar_seek_fill(TALLOC_CTX *mem_ctx,
                        return NT_STATUS_INTERNAL_ERROR;
                }
 
+               if (qar_array_blob->length + sizeof(qar_buf) > in_max_output) {
+                       /*
+                        * Earlier check ensures space for one range or more.
+                        * Subsequent overflow results in a truncated response.
+                        */
+                       DBG_NOTICE("truncated QAR output: need > %zu, max %zu\n",
+                               qar_array_blob->length + sizeof(qar_buf),
+                               in_max_output);
+                       return STATUS_BUFFER_OVERFLOW;
+               }
+
                qar_buf.file_off = data_off;
                /* + 1 to convert maximum offset to length */
                qar_buf.len = MIN(hole_off, max_off + 1) - data_off;
@@ -652,6 +664,13 @@ static NTSTATUS fsctl_qar(TALLOC_CTX *mem_ctx,
                return NT_STATUS_INVALID_PARAMETER;
        }
 
+       /* must have enough space for at least one range */
+       if (in_max_output < sizeof(struct file_alloced_range_buf)) {
+               DEBUG(2, ("QAR max %lu insufficient for one range\n",
+                         (unsigned long)in_max_output));
+               return NT_STATUS_BUFFER_TOO_SMALL;
+       }
+
        /*
         * Maximum offset is either the last valid offset _before_ EOF, or the
         * last byte offset within the requested range. -1 converts length to
@@ -687,31 +706,24 @@ static NTSTATUS fsctl_qar(TALLOC_CTX *mem_ctx,
                status = fsctl_qar_buf_push(mem_ctx, &qar_buf, &qar_array_blob);
        } else {
                status = fsctl_qar_seek_fill(mem_ctx, fsp, qar_req.buf.file_off,
-                                            max_off, &qar_array_blob);
-       }
-       if (!NT_STATUS_IS_OK(status)) {
-               return status;
+                                            max_off, in_max_output,
+                                            &qar_array_blob);
        }
 
-       /* marshall response buffer. */
-       qar_rsp.far_buf_array = qar_array_blob;
+       if (NT_STATUS_IS_OK(status)
+        || NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+               /* marshall response. STATUS_BUFFER_OVERFLOW=truncated */
+               qar_rsp.far_buf_array = qar_array_blob;
 
-       ndr_ret = ndr_push_struct_blob(out_output, mem_ctx, &qar_rsp,
-               (ndr_push_flags_fn_t)ndr_push_fsctl_query_alloced_ranges_rsp);
-       if (ndr_ret != NDR_ERR_SUCCESS) {
-               DEBUG(0, ("failed to marshall QAR rsp\n"));
-               return NT_STATUS_INVALID_PARAMETER;
-       }
-
-       if (out_output->length > in_max_output) {
-               DEBUG(2, ("QAR output len %lu exceeds max %lu\n",
-                         (unsigned long)out_output->length,
-                         (unsigned long)in_max_output));
-               data_blob_free(out_output);
-               return NT_STATUS_BUFFER_TOO_SMALL;
+               ndr_ret = ndr_push_struct_blob(out_output, mem_ctx, &qar_rsp,
+                (ndr_push_flags_fn_t)ndr_push_fsctl_query_alloced_ranges_rsp);
+               if (ndr_ret != NDR_ERR_SUCCESS) {
+                       DEBUG(0, ("failed to marshall QAR rsp\n"));
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
        }
 
-       return NT_STATUS_OK;
+       return status;
 }
 
 static void smb2_ioctl_filesys_dup_extents_done(struct tevent_req *subreq);
index fe74dfecd8e6f2e6d425a6c1793ba4dfef69a681..949626918108ca1bcd7127deb1505dac9563fa3a 100644 (file)
@@ -86,7 +86,8 @@ static bool smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status,
        if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)
         && ((ctl_code == FSCTL_PIPE_TRANSCEIVE)
          || (ctl_code == FSCTL_PIPE_PEEK)
-         || (ctl_code == FSCTL_DFS_GET_REFERRALS))) {
+         || (ctl_code == FSCTL_DFS_GET_REFERRALS)
+         || (ctl_code == FSCTL_QUERY_ALLOCATED_RANGES))) {
                return false;
        }