Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Copyright (C) Ralph Boehme 2017
5 :
6 : This program is free software; you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation; either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include "includes.h"
21 : #include "smbd/smbd.h"
22 : #include "smbd/globals.h"
23 : #include "../libcli/security/security.h"
24 : #include "dbwrap/dbwrap.h"
25 : #include "dbwrap/dbwrap_rbt.h"
26 : #include "dbwrap/dbwrap_open.h"
27 : #include "../lib/util/util_tdb.h"
28 : #include "librpc/gen_ndr/ndr_ioctl.h"
29 : #include "librpc/gen_ndr/ioctl.h"
30 : #include "offload_token.h"
31 :
32 : struct vfs_offload_ctx {
33 : bool initialized;
34 : struct db_context *db_ctx;
35 : };
36 :
37 320 : NTSTATUS vfs_offload_token_ctx_init(TALLOC_CTX *mem_ctx,
38 : struct vfs_offload_ctx **_ctx)
39 : {
40 320 : struct vfs_offload_ctx *ctx = *_ctx;
41 :
42 320 : if (ctx != NULL) {
43 88 : if (!ctx->initialized) {
44 0 : return NT_STATUS_INTERNAL_ERROR;
45 : }
46 88 : return NT_STATUS_OK;
47 : }
48 :
49 232 : ctx = talloc_zero(mem_ctx, struct vfs_offload_ctx);
50 232 : if (ctx == NULL) {
51 0 : return NT_STATUS_NO_MEMORY;
52 : }
53 :
54 232 : ctx->db_ctx = db_open_rbt(mem_ctx);
55 232 : if (ctx->db_ctx == NULL) {
56 0 : TALLOC_FREE(ctx);
57 0 : return NT_STATUS_INTERNAL_ERROR;
58 : }
59 :
60 232 : ctx->initialized = true;
61 232 : *_ctx = ctx;
62 232 : return NT_STATUS_OK;
63 : }
64 :
65 : struct fsp_token_link {
66 : struct vfs_offload_ctx *ctx;
67 : DATA_BLOB token_blob;
68 : };
69 :
70 320 : static int fsp_token_link_destructor(struct fsp_token_link *link)
71 : {
72 320 : DATA_BLOB token_blob = link->token_blob;
73 320 : TDB_DATA key = make_tdb_data(token_blob.data, token_blob.length);
74 0 : NTSTATUS status;
75 :
76 320 : status = dbwrap_delete(link->ctx->db_ctx, key);
77 320 : if (!NT_STATUS_IS_OK(status)) {
78 0 : DBG_ERR("dbwrap_delete failed: %s. Token:\n", nt_errstr(status));
79 0 : dump_data(0, token_blob.data, token_blob.length);
80 0 : return -1;
81 : }
82 :
83 320 : return 0;
84 : }
85 :
86 : struct vfs_offload_token_db_store_fsp_state {
87 : const struct files_struct *fsp;
88 : const DATA_BLOB *token_blob;
89 : NTSTATUS status;
90 : };
91 :
92 320 : static void vfs_offload_token_db_store_fsp_fn(
93 : struct db_record *rec, TDB_DATA value, void *private_data)
94 : {
95 320 : struct vfs_offload_token_db_store_fsp_state *state = private_data;
96 320 : const struct files_struct *fsp = state->fsp;
97 320 : const DATA_BLOB *token_blob = state->token_blob;
98 320 : files_struct *token_db_fsp = NULL;
99 320 : void *ptr = NULL;
100 :
101 320 : if (value.dsize == 0) {
102 312 : value = make_tdb_data((uint8_t *)&fsp, sizeof(files_struct *));
103 312 : state->status = dbwrap_record_store(rec, value, 0);
104 312 : return;
105 : }
106 :
107 8 : if (value.dsize != sizeof(ptr)) {
108 0 : DBG_ERR("Bad db entry for token:\n");
109 0 : dump_data(1, token_blob->data, token_blob->length);
110 0 : state->status = NT_STATUS_INTERNAL_ERROR;
111 0 : return;
112 : }
113 8 : memcpy(&ptr, value.dptr, value.dsize);
114 :
115 8 : token_db_fsp = talloc_get_type_abort(ptr, struct files_struct);
116 8 : if (token_db_fsp != fsp) {
117 0 : DBG_ERR("token for fsp [%s] matches already known "
118 : "but different fsp [%s]:\n",
119 : fsp_str_dbg(fsp),
120 : fsp_str_dbg(token_db_fsp));
121 0 : dump_data(1, token_blob->data, token_blob->length);
122 0 : state->status = NT_STATUS_INTERNAL_ERROR;
123 0 : return;
124 : }
125 : }
126 :
127 320 : NTSTATUS vfs_offload_token_db_store_fsp(struct vfs_offload_ctx *ctx,
128 : const files_struct *fsp,
129 : const DATA_BLOB *token_blob)
130 : {
131 320 : struct vfs_offload_token_db_store_fsp_state state = {
132 : .fsp = fsp, .token_blob = token_blob,
133 : };
134 320 : struct fsp_token_link *link = NULL;
135 320 : TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
136 0 : NTSTATUS status;
137 :
138 320 : link = talloc(fsp, struct fsp_token_link);
139 320 : if (link == NULL) {
140 0 : return NT_STATUS_NO_MEMORY;
141 : }
142 320 : *link = (struct fsp_token_link) {
143 : .ctx = ctx,
144 320 : .token_blob = data_blob_dup_talloc(link, *token_blob),
145 : };
146 320 : if (link->token_blob.data == NULL) {
147 0 : TALLOC_FREE(link);
148 0 : return NT_STATUS_NO_MEMORY;
149 : }
150 :
151 320 : status = dbwrap_do_locked(
152 : ctx->db_ctx,
153 : key,
154 : vfs_offload_token_db_store_fsp_fn,
155 : &state);
156 320 : if (!NT_STATUS_IS_OK(status)) {
157 0 : DBG_DEBUG("dbwrap_do_locked failed: %s\n",
158 : nt_errstr(status));
159 0 : TALLOC_FREE(link);
160 0 : return status;
161 : }
162 320 : if (!NT_STATUS_IS_OK(state.status)) {
163 0 : DBG_DEBUG("vfs_offload_token_db_store_fsp_fn failed: %s\n",
164 : nt_errstr(status));
165 0 : TALLOC_FREE(link);
166 0 : return status;
167 : }
168 :
169 320 : talloc_set_destructor(link, fsp_token_link_destructor);
170 320 : return NT_STATUS_OK;
171 : }
172 :
173 : struct vfs_offload_token_db_fetch_fsp_state {
174 : struct files_struct **fsp;
175 : NTSTATUS status;
176 : };
177 :
178 296 : static void vfs_offload_token_db_fetch_fsp_fn(
179 : TDB_DATA key, TDB_DATA value, void *private_data)
180 : {
181 296 : struct vfs_offload_token_db_fetch_fsp_state *state = private_data;
182 0 : void *ptr;
183 :
184 296 : if (value.dsize != sizeof(ptr)) {
185 0 : DBG_ERR("Bad db entry for token:\n");
186 0 : dump_data(1, key.dptr, key.dsize);
187 0 : state->status = NT_STATUS_INTERNAL_ERROR;
188 0 : return;
189 : }
190 :
191 296 : memcpy(&ptr, value.dptr, value.dsize);
192 296 : *state->fsp = talloc_get_type_abort(ptr, struct files_struct);
193 : }
194 :
195 312 : NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
196 : const DATA_BLOB *token_blob,
197 : files_struct **fsp)
198 : {
199 312 : struct vfs_offload_token_db_fetch_fsp_state state = { .fsp = fsp };
200 312 : TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
201 0 : NTSTATUS status;
202 :
203 312 : status = dbwrap_parse_record(
204 : ctx->db_ctx,
205 : key,
206 : vfs_offload_token_db_fetch_fsp_fn,
207 : &state);
208 312 : if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
209 16 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
210 : }
211 312 : if (!NT_STATUS_IS_OK(status)) {
212 16 : DBG_DEBUG("Unknown token:\n");
213 16 : dump_data(10, token_blob->data, token_blob->length);
214 16 : return status;
215 : }
216 296 : return state.status;
217 : }
218 :
219 280 : NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
220 : const files_struct *fsp,
221 : uint32_t fsctl,
222 : DATA_BLOB *token_blob)
223 : {
224 0 : size_t len;
225 :
226 280 : switch (fsctl) {
227 0 : case FSCTL_DUP_EXTENTS_TO_FILE:
228 0 : len = 20;
229 0 : break;
230 280 : case FSCTL_SRV_REQUEST_RESUME_KEY:
231 280 : len = 24;
232 280 : break;
233 0 : default:
234 0 : DBG_ERR("Invalid fsctl [%" PRIu32 "]\n", fsctl);
235 0 : return NT_STATUS_NOT_SUPPORTED;
236 : }
237 :
238 280 : *token_blob = data_blob_talloc_zero(mem_ctx, len);
239 280 : if (token_blob->length == 0) {
240 0 : return NT_STATUS_NO_MEMORY;
241 : }
242 :
243 : /* combine persistent and volatile handles for the resume key */
244 280 : SBVAL(token_blob->data,
245 : SMB_VFS_ODX_TOKEN_OFFSET_PFID,
246 : fsp->op->global->open_persistent_id);
247 280 : SBVAL(token_blob->data,
248 : SMB_VFS_ODX_TOKEN_OFFSET_VFID,
249 : fsp->op->global->open_volatile_id);
250 280 : SIVAL(token_blob->data,
251 : SMB_VFS_ODX_TOKEN_OFFSET_FSCTL,
252 : fsctl);
253 :
254 280 : return NT_STATUS_OK;
255 : }
256 :
257 :
258 288 : NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
259 : files_struct *src_fsp,
260 : files_struct *dst_fsp)
261 : {
262 0 : NTSTATUS status;
263 :
264 288 : if (src_fsp->vuid != dst_fsp->vuid) {
265 0 : DBG_INFO("copy chunk handles not in the same session.\n");
266 0 : return NT_STATUS_ACCESS_DENIED;
267 : }
268 :
269 288 : if (!NT_STATUS_IS_OK(src_fsp->op->status)) {
270 0 : DBG_INFO("copy chunk source handle invalid: %s\n",
271 : nt_errstr(src_fsp->op->status));
272 0 : return NT_STATUS_ACCESS_DENIED;
273 : }
274 :
275 288 : if (!NT_STATUS_IS_OK(dst_fsp->op->status)) {
276 0 : DBG_INFO("copy chunk destination handle invalid: %s\n",
277 : nt_errstr(dst_fsp->op->status));
278 0 : return NT_STATUS_ACCESS_DENIED;
279 : }
280 :
281 288 : if (src_fsp->fsp_flags.closing) {
282 0 : DBG_INFO("copy chunk src handle with closing in progress.\n");
283 0 : return NT_STATUS_ACCESS_DENIED;
284 : }
285 :
286 288 : if (dst_fsp->fsp_flags.closing) {
287 0 : DBG_INFO("copy chunk dst handle with closing in progress.\n");
288 0 : return NT_STATUS_ACCESS_DENIED;
289 : }
290 :
291 288 : if (src_fsp->fsp_flags.is_directory) {
292 0 : DBG_INFO("copy chunk no read on src directory handle (%s).\n",
293 : smb_fname_str_dbg(src_fsp->fsp_name));
294 0 : return NT_STATUS_ACCESS_DENIED;
295 : }
296 :
297 288 : if (dst_fsp->fsp_flags.is_directory) {
298 0 : DBG_INFO("copy chunk no read on dst directory handle (%s).\n",
299 : smb_fname_str_dbg(dst_fsp->fsp_name));
300 0 : return NT_STATUS_ACCESS_DENIED;
301 : }
302 :
303 288 : if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
304 0 : DBG_INFO("copy chunk no access on IPC$ handle.\n");
305 0 : return NT_STATUS_ACCESS_DENIED;
306 : }
307 :
308 288 : if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
309 0 : DBG_INFO("copy chunk no access on PRINT handle.\n");
310 0 : return NT_STATUS_ACCESS_DENIED;
311 : }
312 :
313 : /*
314 : * [MS-SMB2] 3.3.5.15.6 Handling a Server-Side Data Copy Request
315 : * The server MUST fail the request with STATUS_ACCESS_DENIED if any of
316 : * the following are true:
317 : * - The Open.GrantedAccess of the destination file does not include
318 : * FILE_WRITE_DATA or FILE_APPEND_DATA.
319 : *
320 : * A non writable dst handle also doesn't make sense for other fsctls.
321 : */
322 288 : status = check_any_access_fsp(dst_fsp, FILE_WRITE_DATA|FILE_APPEND_DATA);
323 288 : if (!NT_STATUS_IS_OK(status)) {
324 8 : DBG_INFO("dest handle not writable (%s).\n",
325 : smb_fname_str_dbg(dst_fsp->fsp_name));
326 8 : return status;
327 : }
328 : /*
329 : * - The Open.GrantedAccess of the destination file does not include
330 : * FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
331 : */
332 280 : if ((fsctl == FSCTL_SRV_COPYCHUNK) && !CHECK_READ_IOCTL(dst_fsp)) {
333 8 : DBG_INFO("copy chunk no read on dest handle (%s).\n",
334 : smb_fname_str_dbg(dst_fsp->fsp_name));
335 8 : return NT_STATUS_ACCESS_DENIED;
336 : }
337 : /*
338 : * - The Open.GrantedAccess of the source file does not include
339 : * FILE_READ_DATA access.
340 : */
341 272 : if (!CHECK_READ_SMB2(src_fsp)) {
342 8 : DBG_INFO("src handle not readable (%s).\n",
343 : smb_fname_str_dbg(src_fsp->fsp_name));
344 8 : return NT_STATUS_ACCESS_DENIED;
345 : }
346 :
347 264 : return NT_STATUS_OK;
348 : }
|