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 : }
|