s3:libsmb: allow store_cldap_reply() to work with a ipv6 response
[samba.git] / source3 / modules / offload_token.c
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 NTSTATUS vfs_offload_token_ctx_init(TALLOC_CTX *mem_ctx,
38                                     struct vfs_offload_ctx **_ctx)
39 {
40         struct vfs_offload_ctx *ctx = *_ctx;
41
42         if (ctx != NULL) {
43                 if (!ctx->initialized) {
44                         return NT_STATUS_INTERNAL_ERROR;
45                 }
46                 return NT_STATUS_OK;
47         }
48
49         ctx = talloc_zero(mem_ctx, struct vfs_offload_ctx);
50         if (ctx == NULL) {
51                 return NT_STATUS_NO_MEMORY;
52         }
53
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;
58         }
59
60         ctx->initialized = true;
61         *_ctx = ctx;
62         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 static int fsp_token_link_destructor(struct fsp_token_link *link)
71 {
72         DATA_BLOB token_blob = link->token_blob;
73         TDB_DATA key = make_tdb_data(token_blob.data, token_blob.length);
74         NTSTATUS status;
75
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;
81         }
82
83         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 static void vfs_offload_token_db_store_fsp_fn(
93         struct db_record *rec, TDB_DATA value, void *private_data)
94 {
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;
100
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;
105         }
106
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;
112         }
113         memcpy(&ptr, value.dptr, value.dsize);
114
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;
124         }
125 }
126
127 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         struct vfs_offload_token_db_store_fsp_state state = {
132                 .fsp = fsp, .token_blob = token_blob,
133         };
134         struct fsp_token_link *link = NULL;
135         TDB_DATA key = make_tdb_data(token_blob->data, token_blob->length);
136         NTSTATUS status;
137
138         link = talloc(fsp, struct fsp_token_link);
139         if (link == NULL) {
140                 return NT_STATUS_NO_MEMORY;
141         }
142         *link = (struct fsp_token_link) {
143                 .ctx = ctx,
144                 .token_blob = data_blob_dup_talloc(link, *token_blob),
145         };
146         if (link->token_blob.data == NULL) {
147                 TALLOC_FREE(link);
148                 return NT_STATUS_NO_MEMORY;
149         }
150
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;
161         }
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;
167         }
168
169         talloc_set_destructor(link, fsp_token_link_destructor);
170         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 static void vfs_offload_token_db_fetch_fsp_fn(
179         TDB_DATA key, TDB_DATA value, void *private_data)
180 {
181         struct vfs_offload_token_db_fetch_fsp_state *state = private_data;
182         void *ptr;
183
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;
189         }
190
191         memcpy(&ptr, value.dptr, value.dsize);
192         *state->fsp = talloc_get_type_abort(ptr, struct files_struct);
193 }
194
195 NTSTATUS vfs_offload_token_db_fetch_fsp(struct vfs_offload_ctx *ctx,
196                                         const DATA_BLOB *token_blob,
197                                         files_struct **fsp)
198 {
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;
202
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;
210         }
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;
215         }
216         return state.status;
217 }
218
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)
223 {
224         size_t len;
225
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;
236         }
237
238         *token_blob = data_blob_talloc_zero(mem_ctx, len);
239         if (token_blob->length == 0) {
240                 return NT_STATUS_NO_MEMORY;
241         }
242
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);
253
254         return NT_STATUS_OK;
255 }
256
257
258 NTSTATUS vfs_offload_token_check_handles(uint32_t fsctl,
259                                          files_struct *src_fsp,
260                                          files_struct *dst_fsp)
261 {
262         NTSTATUS status;
263
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;
267         }
268
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;
273         }
274
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;
279         }
280
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;
284         }
285
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;
289         }
290
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;
295         }
296
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;
301         }
302
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;
306         }
307
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;
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         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;
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         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;
336         }
337         /*
338          * - The Open.GrantedAccess of the source file does not include
339          *   FILE_READ_DATA access.
340          */
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;
345         }
346
347         return NT_STATUS_OK;
348 }