smbd: replace CHECK_WRITE() macro with calls to check_any_access_fsp()
[Samba.git] / source3 / modules / offload_token.c
blob3b71a0028fb77f8ecb3045ed3ef6f1116b488a7b
1 /*
2 Unix SMB/CIFS implementation.
4 Copyright (C) Ralph Boehme 2017
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.
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.
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/>.
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"
32 struct vfs_offload_ctx {
33 bool initialized;
34 struct db_context *db_ctx;
37 NTSTATUS vfs_offload_token_ctx_init(TALLOC_CTX *mem_ctx,
38 struct vfs_offload_ctx **_ctx)
40 struct vfs_offload_ctx *ctx = *_ctx;
42 if (ctx != NULL) {
43 if (!ctx->initialized) {
44 return NT_STATUS_INTERNAL_ERROR;
46 return NT_STATUS_OK;
49 ctx = talloc_zero(mem_ctx, struct vfs_offload_ctx);
50 if (ctx == NULL) {
51 return NT_STATUS_NO_MEMORY;
54 ctx->db_ctx = db_open_rbt(mem_ctx);
55 if (ctx->db_ctx == NULL) {
56 TALLOC_FREE(ctx);
57 return NT_STATUS_INTERNAL_ERROR;
60 ctx->initialized = true;
61 *_ctx = ctx;
62 return NT_STATUS_OK;
65 struct fsp_token_link {
66 struct vfs_offload_ctx *ctx;
67 DATA_BLOB token_blob;
70 static int fsp_token_link_destructor(struct fsp_token_link *link)
72 DATA_BLOB token_blob = link->token_blob;
73 TDB_DATA key = make_tdb_data(token_blob.data, token_blob.length);
74 NTSTATUS status;
76 status = dbwrap_delete(link->ctx->db_ctx, key);
77 if (!NT_STATUS_IS_OK(status)) {
78 DBG_ERR("dbwrap_delete failed: %s. Token:\n", nt_errstr(status));
79 dump_data(0, token_blob.data, token_blob.length);
80 return -1;
83 return 0;
86 struct vfs_offload_token_db_store_fsp_state {
87 const struct files_struct *fsp;
88 const DATA_BLOB *token_blob;
89 NTSTATUS status;
92 static void vfs_offload_token_db_store_fsp_fn(
93 struct db_record *rec, TDB_DATA value, void *private_data)
95 struct vfs_offload_token_db_store_fsp_state *state = private_data;
96 const struct files_struct *fsp = state->fsp;
97 const DATA_BLOB *token_blob = state->token_blob;
98 files_struct *token_db_fsp = NULL;
99 void *ptr = NULL;
101 if (value.dsize == 0) {
102 value = make_tdb_data((uint8_t *)&fsp, sizeof(files_struct *));
103 state->status = dbwrap_record_store(rec, value, 0);
104 return;
107 if (value.dsize != sizeof(ptr)) {
108 DBG_ERR("Bad db entry for token:\n");
109 dump_data(1, token_blob->data, token_blob->length);
110 state->status = NT_STATUS_INTERNAL_ERROR;
111 return;
113 memcpy(&ptr, value.dptr, value.dsize);
115 token_db_fsp = talloc_get_type_abort(ptr, struct files_struct);
116 if (token_db_fsp != fsp) {
117 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 dump_data(1, token_blob->data, token_blob->length);
122 state->status = NT_STATUS_INTERNAL_ERROR;
123 return;
127 NTSTATUS vfs_offload_token_db_store_fsp(struct vfs_offload_ctx *ctx,
128 const files_struct *fsp,
129 const DATA_BLOB *token_blob)
131 struct vfs_offload_token_db_store_fsp_state state = {
132 .fsp = fsp, .token_blob = token_blob,
134 struct fsp_token_link *link = NULL;
135 TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
136 NTSTATUS status;
138 link = talloc(fsp, struct fsp_token_link);
139 if (link == NULL) {
140 return NT_STATUS_NO_MEMORY;
142 *link = (struct fsp_token_link) {
143 .ctx = ctx,
144 .token_blob = data_blob_dup_talloc(link, *token_blob),
146 if (link->token_blob.data == NULL) {
147 TALLOC_FREE(link);
148 return NT_STATUS_NO_MEMORY;
151 status = dbwrap_do_locked(
152 ctx->db_ctx,
153 key,
154 vfs_offload_token_db_store_fsp_fn,
155 &state);
156 if (!NT_STATUS_IS_OK(status)) {
157 DBG_DEBUG("dbwrap_do_locked failed: %s\n",
158 nt_errstr(status));
159 TALLOC_FREE(link);
160 return status;
162 if (!NT_STATUS_IS_OK(state.status)) {
163 DBG_DEBUG("vfs_offload_token_db_store_fsp_fn failed: %s\n",
164 nt_errstr(status));
165 TALLOC_FREE(link);
166 return status;
169 talloc_set_destructor(link, fsp_token_link_destructor);
170 return NT_STATUS_OK;
173 struct vfs_offload_token_db_fetch_fsp_state {
174 struct files_struct **fsp;
175 NTSTATUS status;
178 static void vfs_offload_token_db_fetch_fsp_fn(
179 TDB_DATA key, TDB_DATA value, void *private_data)
181 struct vfs_offload_token_db_fetch_fsp_state *state = private_data;
182 void *ptr;
184 if (value.dsize != sizeof(ptr)) {
185 DBG_ERR("Bad db entry for token:\n");
186 dump_data(1, key.dptr, key.dsize);
187 state->status = NT_STATUS_INTERNAL_ERROR;
188 return;
191 memcpy(&ptr, value.dptr, value.dsize);
192 *state->fsp = talloc_get_type_abort(ptr, struct files_struct);
195 NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
196 const DATA_BLOB *token_blob,
197 files_struct **fsp)
199 struct vfs_offload_token_db_fetch_fsp_state state = { .fsp = fsp };
200 TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
201 NTSTATUS status;
203 status = dbwrap_parse_record(
204 ctx->db_ctx,
205 key,
206 vfs_offload_token_db_fetch_fsp_fn,
207 &state);
208 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
209 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
211 if (!NT_STATUS_IS_OK(status)) {
212 DBG_DEBUG("Unknown token:\n");
213 dump_data(10, token_blob->data, token_blob->length);
214 return status;
216 return state.status;
219 NTSTATUS vfs_offload_token_create_blob(TALLOC_CTX *mem_ctx,
220 const files_struct *fsp,
221 uint32_t fsctl,
222 DATA_BLOB *token_blob)
224 size_t len;
226 switch (fsctl) {
227 case FSCTL_DUP_EXTENTS_TO_FILE:
228 len = 20;
229 break;
230 case FSCTL_SRV_REQUEST_RESUME_KEY:
231 len = 24;
232 break;
233 default:
234 DBG_ERR("Invalid fsctl [%" PRIu32 "]\n", fsctl);
235 return NT_STATUS_NOT_SUPPORTED;
238 *token_blob = data_blob_talloc_zero(mem_ctx, len);
239 if (token_blob->length == 0) {
240 return NT_STATUS_NO_MEMORY;
243 /* combine persistent and volatile handles for the resume key */
244 SBVAL(token_blob->data,
245 SMB_VFS_ODX_TOKEN_OFFSET_PFID,
246 fsp->op->global->open_persistent_id);
247 SBVAL(token_blob->data,
248 SMB_VFS_ODX_TOKEN_OFFSET_VFID,
249 fsp->op->global->open_volatile_id);
250 SIVAL(token_blob->data,
251 SMB_VFS_ODX_TOKEN_OFFSET_FSCTL,
252 fsctl);
254 return NT_STATUS_OK;
258 NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
259 files_struct *src_fsp,
260 files_struct *dst_fsp)
262 NTSTATUS status;
264 if (src_fsp->vuid != dst_fsp->vuid) {
265 DBG_INFO("copy chunk handles not in the same session.\n");
266 return NT_STATUS_ACCESS_DENIED;
269 if (!NT_STATUS_IS_OK(src_fsp->op->status)) {
270 DBG_INFO("copy chunk source handle invalid: %s\n",
271 nt_errstr(src_fsp->op->status));
272 return NT_STATUS_ACCESS_DENIED;
275 if (!NT_STATUS_IS_OK(dst_fsp->op->status)) {
276 DBG_INFO("copy chunk destination handle invalid: %s\n",
277 nt_errstr(dst_fsp->op->status));
278 return NT_STATUS_ACCESS_DENIED;
281 if (src_fsp->fsp_flags.closing) {
282 DBG_INFO("copy chunk src handle with closing in progress.\n");
283 return NT_STATUS_ACCESS_DENIED;
286 if (dst_fsp->fsp_flags.closing) {
287 DBG_INFO("copy chunk dst handle with closing in progress.\n");
288 return NT_STATUS_ACCESS_DENIED;
291 if (src_fsp->fsp_flags.is_directory) {
292 DBG_INFO("copy chunk no read on src directory handle (%s).\n",
293 smb_fname_str_dbg(src_fsp->fsp_name));
294 return NT_STATUS_ACCESS_DENIED;
297 if (dst_fsp->fsp_flags.is_directory) {
298 DBG_INFO("copy chunk no read on dst directory handle (%s).\n",
299 smb_fname_str_dbg(dst_fsp->fsp_name));
300 return NT_STATUS_ACCESS_DENIED;
303 if (IS_IPC(src_fsp->conn) || IS_IPC(dst_fsp->conn)) {
304 DBG_INFO("copy chunk no access on IPC$ handle.\n");
305 return NT_STATUS_ACCESS_DENIED;
308 if (IS_PRINT(src_fsp->conn) || IS_PRINT(dst_fsp->conn)) {
309 DBG_INFO("copy chunk no access on PRINT handle.\n");
310 return NT_STATUS_ACCESS_DENIED;
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.
320 * A non writable dst handle also doesn't make sense for other fsctls.
322 status = check_any_access_fsp(dst_fsp, FILE_WRITE_DATA|FILE_APPEND_DATA);
323 if (!NT_STATUS_IS_OK(status)) {
324 DBG_INFO("dest handle not writable (%s).\n",
325 smb_fname_str_dbg(dst_fsp->fsp_name));
326 return status;
329 * - The Open.GrantedAccess of the destination file does not include
330 * FILE_READ_DATA, and the CtlCode is FSCTL_SRV_COPYCHUNK.
332 if ((fsctl == FSCTL_SRV_COPYCHUNK) && !CHECK_READ_IOCTL(dst_fsp)) {
333 DBG_INFO("copy chunk no read on dest handle (%s).\n",
334 smb_fname_str_dbg(dst_fsp->fsp_name));
335 return NT_STATUS_ACCESS_DENIED;
338 * - The Open.GrantedAccess of the source file does not include
339 * FILE_READ_DATA access.
341 if (!CHECK_READ_SMB2(src_fsp)) {
342 DBG_INFO("src handle not readable (%s).\n",
343 smb_fname_str_dbg(src_fsp->fsp_name));
344 return NT_STATUS_ACCESS_DENIED;
347 return NT_STATUS_OK;