LCOV - code coverage report
Current view: top level - source3/modules - util_reparse.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 77 123 62.6 %
Date: 2024-05-31 13:13:24 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Unix SMB/CIFS implementation.
       3             :  * Utility functions for reparse points.
       4             :  *
       5             :  * Copyright (C) Jeremy Allison 2018
       6             :  *
       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.
      11             :  *
      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.
      16             :  *
      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/>.
      19             :  */
      20             : 
      21             : #include "includes.h"
      22             : #include "util_reparse.h"
      23             : #include "libcli/smb/reparse.h"
      24             : #include "source3/smbd/proto.h"
      25             : 
      26          16 : static NTSTATUS fsctl_get_reparse_point_reg(struct files_struct *fsp,
      27             :                                             TALLOC_CTX *ctx,
      28             :                                             uint8_t **_out_data,
      29             :                                             uint32_t max_out_len,
      30             :                                             uint32_t *_out_len)
      31             : {
      32          16 :         uint8_t *val = NULL;
      33           0 :         ssize_t sizeret;
      34           0 :         NTSTATUS status;
      35             : 
      36             :         /*
      37             :          * 64k+8 bytes is the maximum reparse point length
      38             :          * possible
      39             :          */
      40             : 
      41          16 :         val = talloc_array(ctx, uint8_t, MIN(max_out_len, 65536 + 8));
      42          16 :         if (val == NULL) {
      43           0 :                 return NT_STATUS_NO_MEMORY;
      44             :         }
      45             : 
      46          16 :         sizeret = SMB_VFS_FGETXATTR(fsp,
      47             :                                     SAMBA_XATTR_REPARSE_ATTRIB,
      48             :                                     val,
      49             :                                     talloc_get_size(val));
      50             : 
      51          16 :         if ((sizeret == -1) && (errno == ERANGE)) {
      52           0 :                 status = NT_STATUS_BUFFER_TOO_SMALL;
      53           0 :                 goto fail;
      54             :         }
      55             : 
      56          16 :         if ((sizeret == -1) && (errno == ENOATTR)) {
      57           0 :                 DBG_DEBUG(SAMBA_XATTR_REPARSE_ATTRIB " does not exist\n");
      58           0 :                 status = NT_STATUS_NOT_A_REPARSE_POINT;
      59           0 :                 goto fail;
      60             :         }
      61             : 
      62          16 :         if (sizeret == -1) {
      63           0 :                 status = map_nt_error_from_unix(errno);
      64           0 :                 DBG_DEBUG("SMB_VFS_FGETXATTR failed: %s\n", strerror(errno));
      65           0 :                 goto fail;
      66             :         }
      67             : 
      68          16 :         *_out_data = val;
      69          16 :         *_out_len = sizeret;
      70          16 :         return NT_STATUS_OK;
      71           0 : fail:
      72           0 :         TALLOC_FREE(val);
      73           0 :         return status;
      74             : }
      75             : 
      76        2238 : NTSTATUS fsctl_get_reparse_point(struct files_struct *fsp,
      77             :                                  TALLOC_CTX *mem_ctx,
      78             :                                  uint32_t *_reparse_tag,
      79             :                                  uint8_t **_out_data,
      80             :                                  uint32_t max_out_len,
      81             :                                  uint32_t *_out_len)
      82             : {
      83           0 :         uint32_t dos_mode;
      84        2238 :         uint8_t *out_data = NULL;
      85        2238 :         uint32_t out_len = 0;
      86        2238 :         uint32_t reparse_tag = 0;
      87        2238 :         const uint8_t *reparse_data = NULL;
      88           0 :         size_t reparse_data_length;
      89        2238 :         NTSTATUS status = NT_STATUS_NOT_A_REPARSE_POINT;
      90             : 
      91        2238 :         dos_mode = fdos_mode(fsp);
      92        2238 :         if ((dos_mode & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
      93        2222 :                 return NT_STATUS_NOT_A_REPARSE_POINT;
      94             :         }
      95             : 
      96          16 :         if (S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
      97          16 :                 DBG_DEBUG("%s is a regular file\n", fsp_str_dbg(fsp));
      98          16 :                 status = fsctl_get_reparse_point_reg(
      99             :                         fsp, mem_ctx, &out_data, max_out_len, &out_len);
     100             :         }
     101             : 
     102          16 :         if (!NT_STATUS_IS_OK(status)) {
     103           0 :                 DBG_DEBUG("failed: %s\n", nt_errstr(status));
     104           0 :                 return status;
     105             :         }
     106             : 
     107          16 :         status = reparse_buffer_check(out_data,
     108             :                                       out_len,
     109             :                                       &reparse_tag,
     110             :                                       &reparse_data,
     111             :                                       &reparse_data_length);
     112          16 :         if (!NT_STATUS_IS_OK(status)) {
     113           0 :                 DBG_DEBUG("Invalid reparse data: %s\n", nt_errstr(status));
     114           0 :                 TALLOC_FREE(out_data);
     115           0 :                 return status;
     116             :         }
     117             : 
     118          16 :         *_reparse_tag = reparse_tag;
     119          16 :         *_out_data = out_data;
     120          16 :         *_out_len = out_len;
     121             : 
     122          16 :         return NT_STATUS_OK;
     123             : }
     124             : 
     125        2176 : NTSTATUS fsctl_get_reparse_tag(struct files_struct *fsp,
     126             :                                uint32_t *_reparse_tag)
     127             : {
     128        2176 :         uint8_t *out_data = NULL;
     129           0 :         uint32_t out_len;
     130           0 :         NTSTATUS status;
     131             : 
     132        2176 :         status = fsctl_get_reparse_point(fsp,
     133             :                                          talloc_tos(),
     134             :                                          _reparse_tag,
     135             :                                          &out_data,
     136             :                                          UINT32_MAX,
     137             :                                          &out_len);
     138        2176 :         TALLOC_FREE(out_data);
     139        2176 :         return status;
     140             : }
     141             : 
     142          62 : NTSTATUS fsctl_set_reparse_point(struct files_struct *fsp,
     143             :                                  TALLOC_CTX *mem_ctx,
     144             :                                  const uint8_t *in_data,
     145             :                                  uint32_t in_len)
     146             : {
     147           0 :         uint32_t reparse_tag;
     148          62 :         const uint8_t *reparse_data = NULL;
     149           0 :         size_t reparse_data_length;
     150           0 :         uint32_t existing_tag;
     151           0 :         NTSTATUS status;
     152           0 :         uint32_t dos_mode;
     153           0 :         int ret;
     154             : 
     155          62 :         DBG_DEBUG("Called on %s\n", fsp_str_dbg(fsp));
     156             : 
     157          62 :         if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) {
     158           4 :                 DBG_DEBUG("Can only set reparse point for regular files\n");
     159           4 :                 return NT_STATUS_ACCESS_DENIED;
     160             :         }
     161             : 
     162          58 :         status = reparse_buffer_check(in_data,
     163             :                                       in_len,
     164             :                                       &reparse_tag,
     165             :                                       &reparse_data,
     166             :                                       &reparse_data_length);
     167          58 :         if (!NT_STATUS_IS_OK(status)) {
     168          38 :                 DBG_DEBUG("check_reparse_data_buffer failed: %s\n",
     169             :                           nt_errstr(status));
     170          38 :                 return status;
     171             :         }
     172             : 
     173          20 :         DBG_DEBUG("reparse tag=%" PRIX32 ", length=%zu\n",
     174             :                   reparse_tag,
     175             :                   reparse_data_length);
     176             : 
     177          20 :         status = fsctl_get_reparse_tag(fsp, &existing_tag);
     178          20 :         if (NT_STATUS_IS_OK(status) && (existing_tag != reparse_tag)) {
     179           2 :                 DBG_DEBUG("Can't overwrite tag %" PRIX32 " with tag %" PRIX32
     180             :                           "\n",
     181             :                           existing_tag,
     182             :                           reparse_tag);
     183           2 :                 return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
     184             :         }
     185             : 
     186             :         /* Store the data */
     187          18 :         ret = SMB_VFS_FSETXATTR(
     188             :                 fsp, SAMBA_XATTR_REPARSE_ATTRIB, in_data, in_len, 0);
     189          18 :         if (ret == -1) {
     190           0 :                 status = map_nt_error_from_unix(errno);
     191           0 :                 DBG_DEBUG("setxattr fail on %s - %s\n",
     192             :                           fsp_str_dbg(fsp),
     193             :                           strerror(errno));
     194           0 :                 return status;
     195             :         }
     196             : 
     197             :         /*
     198             :          * Files with reparse points don't have the ATTR_NORMAL bit
     199             :          * set
     200             :          */
     201          18 :         dos_mode = fdos_mode(fsp);
     202          18 :         dos_mode &= ~FILE_ATTRIBUTE_NORMAL;
     203          18 :         dos_mode |= FILE_ATTRIBUTE_REPARSE_POINT;
     204             : 
     205          18 :         status = SMB_VFS_FSET_DOS_ATTRIBUTES(fsp->conn, fsp, dos_mode);
     206             : 
     207          18 :         if (!NT_STATUS_IS_OK(status)) {
     208           0 :                 DBG_ERR("set reparse attr fail on %s - %s\n",
     209             :                         fsp_str_dbg(fsp),
     210             :                         nt_errstr(status));
     211           0 :                 return status;
     212             :         }
     213             : 
     214          18 :         return NT_STATUS_OK;
     215             : }
     216             : 
     217          10 : NTSTATUS fsctl_del_reparse_point(struct files_struct *fsp,
     218             :                                  TALLOC_CTX *mem_ctx,
     219             :                                  const uint8_t *in_data,
     220             :                                  uint32_t in_len)
     221             : {
     222           0 :         uint32_t existing_tag;
     223           0 :         uint32_t reparse_tag;
     224          10 :         const uint8_t *reparse_data = NULL;
     225           0 :         size_t reparse_data_length;
     226           0 :         NTSTATUS status;
     227           0 :         uint32_t dos_mode;
     228           0 :         int ret;
     229             : 
     230          10 :         status = fsctl_get_reparse_tag(fsp, &existing_tag);
     231          10 :         if (!NT_STATUS_IS_OK(status)) {
     232           2 :                 return status;
     233             :         }
     234             : 
     235           8 :         status = reparse_buffer_check(in_data,
     236             :                                       in_len,
     237             :                                       &reparse_tag,
     238             :                                       &reparse_data,
     239             :                                       &reparse_data_length);
     240           8 :         if (!NT_STATUS_IS_OK(status)) {
     241           0 :                 return status;
     242             :         }
     243           8 :         if (reparse_data_length != 0) {
     244           4 :                 return NT_STATUS_IO_REPARSE_DATA_INVALID;
     245             :         }
     246             : 
     247           4 :         if (existing_tag != reparse_tag) {
     248           2 :                 DBG_DEBUG("Expect correct tag %" PRIX32 ", got tag %" PRIX32
     249             :                           "\n",
     250             :                           existing_tag,
     251             :                           reparse_tag);
     252           2 :                 return NT_STATUS_IO_REPARSE_TAG_MISMATCH;
     253             :         }
     254             : 
     255           2 :         ret = SMB_VFS_FREMOVEXATTR(fsp, SAMBA_XATTR_REPARSE_ATTRIB);
     256           2 :         if (ret == -1) {
     257           0 :                 status = map_nt_error_from_unix(errno);
     258           0 :                 DBG_DEBUG("removexattr fail on %s - %s\n",
     259             :                           fsp_str_dbg(fsp),
     260             :                           strerror(errno));
     261           0 :                 return status;
     262             :         }
     263             : 
     264             :         /*
     265             :          * Files with reparse points don't have the ATTR_NORMAL bit
     266             :          * set
     267             :          */
     268           2 :         dos_mode = fdos_mode(fsp);
     269           2 :         dos_mode &= ~FILE_ATTRIBUTE_REPARSE_POINT;
     270             : 
     271           2 :         status = SMB_VFS_FSET_DOS_ATTRIBUTES(fsp->conn, fsp, dos_mode);
     272             : 
     273           2 :         if (!NT_STATUS_IS_OK(status)) {
     274           0 :                 DBG_ERR("set reparse attr fail on %s - %s\n",
     275             :                         fsp_str_dbg(fsp),
     276             :                         nt_errstr(status));
     277           0 :                 return status;
     278             :         }
     279             : 
     280           2 :         return NT_STATUS_OK;
     281             : }

Generated by: LCOV version 1.14