]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: Implement fsctl_set_reparse_point
authorVolker Lendecke <vl@samba.org>
Fri, 2 Dec 2022 10:56:08 +0000 (11:56 +0100)
committerJeremy Allison <jra@samba.org>
Mon, 6 May 2024 20:55:37 +0000 (20:55 +0000)
Store the data in the "user.SmbReparse" xattr. Only allow this on
regular files. Windows does it for directories too, but we can not
allow this: Setting a symlink reparse point in a xattr on a directory
would go unnoticed by our openat2-optimization. If someone really
needs this, we could have a VFS module disallowing openat2 and doing
the appropriate checks on every openat-call.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
source3/modules/util_reparse.c

index 9a13c11f957fda3af08a877c29ef878425e537ed..77bae00693c7b306063142f3175ed4245f91d096 100644 (file)
@@ -21,6 +21,7 @@
 #include "includes.h"
 #include "util_reparse.h"
 #include "libcli/smb/reparse.h"
+#include "source3/smbd/proto.h"
 
 static NTSTATUS fsctl_get_reparse_point_reg(struct files_struct *fsp,
                                            TALLOC_CTX *ctx,
@@ -128,10 +129,20 @@ NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
        uint32_t reparse_tag;
        const uint8_t *reparse_data = NULL;
        size_t reparse_data_length;
+       uint32_t existing_tag;
+       uint8_t *existing_data = NULL;
+       uint32_t existing_len;
        NTSTATUS status;
+       uint32_t dos_mode;
+       int ret;
 
        DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp));
 
+       if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
+               DBG_DEBUG("Can only set reparse point for regular files\n");
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
        status = reparse_buffer_check(in_data,
                                      in_len,
                                      &reparse_tag,
@@ -147,7 +158,54 @@ NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
                  reparse_tag,
                  reparse_data_length);
 
-       return NT_STATUS_NOT_A_REPARSE_POINT;
+       status = fsctl_get_reparse_point(fsp,
+                                        talloc_tos(),
+                                        &existing_tag,
+                                        &existing_data,
+                                        UINT32_MAX,
+                                        &existing_len);
+       if (NT_STATUS_IS_OK(status)) {
+
+               TALLOC_FREE(existing_data);
+
+               if (existing_tag != reparse_tag) {
+                       DBG_DEBUG("Can't overwrite tag %" PRIX32
+                                 " with tag %" PRIX32 "\n",
+                                 existing_tag,
+                                 reparse_tag);
+                       return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
+               }
+       }
+
+       /* Store the data */
+       ret = SMB_VFS_FSETXATTR(
+               fsp, SAMBA_XATTR_REPARSE_ATTRIB, in_data, in_len, 0);
+       if (ret == -1) {
+               status = map_nt_error_from_unix(errno);
+               DBG_DEBUG("setxattr fail on %s - %s\n",
+                         fsp_str_dbg(fsp),
+                         strerror(errno));
+               return status;
+       }
+
+       /*
+        * Files with reparse points don't have the ATTR_NORMAL bit
+        * set
+        */
+       dos_mode = fdos_mode(fsp);
+       dos_mode &= ~FILE_ATTRIBUTE_NORMAL;
+       dos_mode |= FILE_ATTRIBUTE_REPARSE_POINT;
+
+       status = SMB_VFS_FSET_DOS_ATTRIBUTES(fsp->conn, fsp, dos_mode);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("set reparse attr fail on %s - %s\n",
+                       fsp_str_dbg(fsp),
+                       nt_errstr(status));
+               return status;
+       }
+
+       return NT_STATUS_OK;
 }
 
 NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,