Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 : Core SMB2 server
4 :
5 : Copyright (C) Stefan Metzmacher 2009
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 "smbd/smbd.h"
23 : #include "smbd/globals.h"
24 : #include "../libcli/smb/smb_common.h"
25 : #include "../lib/util/tevent_ntstatus.h"
26 : #include "rpc_server/srv_pipe_hnd.h"
27 :
28 : #undef DBGC_CLASS
29 : #define DBGC_CLASS DBGC_SMB2
30 :
31 : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
32 : struct tevent_context *ev,
33 : struct smbd_smb2_request *smb2req,
34 : struct files_struct *in_fsp,
35 : DATA_BLOB in_data,
36 : uint64_t in_offset,
37 : uint32_t in_flags);
38 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
39 : uint32_t *out_count);
40 :
41 : static void smbd_smb2_request_write_done(struct tevent_req *subreq);
42 46368 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
43 : {
44 46368 : struct smbXsrv_connection *xconn = req->xconn;
45 : NTSTATUS status;
46 : const uint8_t *inbody;
47 : uint16_t in_data_offset;
48 : uint32_t in_data_length;
49 : DATA_BLOB in_data_buffer;
50 : uint64_t in_offset;
51 : uint64_t in_file_id_persistent;
52 : uint64_t in_file_id_volatile;
53 : struct files_struct *in_fsp;
54 : uint32_t in_flags;
55 46368 : size_t in_dyn_len = 0;
56 46368 : uint8_t *in_dyn_ptr = NULL;
57 : struct tevent_req *subreq;
58 :
59 46368 : status = smbd_smb2_request_verify_sizes(req, 0x31);
60 46368 : if (!NT_STATUS_IS_OK(status)) {
61 0 : return smbd_smb2_request_error(req, status);
62 : }
63 46368 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
64 :
65 46368 : in_data_offset = SVAL(inbody, 0x02);
66 46368 : in_data_length = IVAL(inbody, 0x04);
67 46368 : in_offset = BVAL(inbody, 0x08);
68 46368 : in_file_id_persistent = BVAL(inbody, 0x10);
69 46368 : in_file_id_volatile = BVAL(inbody, 0x18);
70 46368 : in_flags = IVAL(inbody, 0x2C);
71 :
72 46368 : if (in_data_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
73 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
74 : }
75 :
76 46368 : if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) {
77 0 : in_dyn_ptr = NULL;
78 0 : in_dyn_len = req->smb1req->unread_bytes;
79 : } else {
80 46368 : in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
81 46368 : in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
82 : }
83 :
84 46368 : if (in_data_length > in_dyn_len) {
85 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
86 : }
87 :
88 : /* check the max write size */
89 46368 : if (in_data_length > xconn->smb2.server.max_write) {
90 0 : DEBUG(2,("smbd_smb2_request_process_write : "
91 : "client ignored max write :%s: 0x%08X: 0x%08X\n",
92 : __location__, in_data_length, xconn->smb2.server.max_write));
93 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
94 : }
95 :
96 : /*
97 : * Note: that in_dyn_ptr is NULL for the recvfile case.
98 : */
99 46368 : in_data_buffer.data = in_dyn_ptr;
100 46368 : in_data_buffer.length = in_data_length;
101 :
102 46368 : status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
103 46368 : if (!NT_STATUS_IS_OK(status)) {
104 0 : return smbd_smb2_request_error(req, status);
105 : }
106 :
107 46368 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
108 46368 : if (in_fsp == NULL) {
109 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
110 : }
111 :
112 46368 : subreq = smbd_smb2_write_send(req, req->sconn->ev_ctx,
113 : req, in_fsp,
114 : in_data_buffer,
115 : in_offset,
116 : in_flags);
117 46368 : if (subreq == NULL) {
118 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
119 : }
120 46368 : tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
121 :
122 46368 : return smbd_smb2_request_pending_queue(req, subreq, 500);
123 : }
124 :
125 46364 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
126 : {
127 46364 : struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
128 : struct smbd_smb2_request);
129 : DATA_BLOB outbody;
130 : DATA_BLOB outdyn;
131 46364 : uint32_t out_count = 0;
132 : NTSTATUS status;
133 : NTSTATUS error; /* transport error */
134 :
135 46364 : status = smbd_smb2_write_recv(subreq, &out_count);
136 46364 : TALLOC_FREE(subreq);
137 46364 : if (!NT_STATUS_IS_OK(status)) {
138 2408 : error = smbd_smb2_request_error(req, status);
139 2408 : if (!NT_STATUS_IS_OK(error)) {
140 0 : smbd_server_connection_terminate(req->xconn,
141 : nt_errstr(error));
142 48 : return;
143 : }
144 2408 : return;
145 : }
146 :
147 43956 : outbody = smbd_smb2_generate_outbody(req, 0x10);
148 43956 : if (outbody.data == NULL) {
149 0 : error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
150 0 : if (!NT_STATUS_IS_OK(error)) {
151 0 : smbd_server_connection_terminate(req->xconn,
152 : nt_errstr(error));
153 0 : return;
154 : }
155 0 : return;
156 : }
157 :
158 43956 : SSVAL(outbody.data, 0x00, 0x10 + 1); /* struct size */
159 43956 : SSVAL(outbody.data, 0x02, 0); /* reserved */
160 43956 : SIVAL(outbody.data, 0x04, out_count); /* count */
161 43956 : SIVAL(outbody.data, 0x08, 0); /* remaining */
162 43956 : SSVAL(outbody.data, 0x0C, 0); /* write channel info offset */
163 43956 : SSVAL(outbody.data, 0x0E, 0); /* write channel info length */
164 :
165 43956 : outdyn = data_blob_const(NULL, 0);
166 :
167 43956 : error = smbd_smb2_request_done(req, outbody, &outdyn);
168 43956 : if (!NT_STATUS_IS_OK(error)) {
169 0 : smbd_server_connection_terminate(req->xconn, nt_errstr(error));
170 0 : return;
171 : }
172 : }
173 :
174 : struct smbd_smb2_write_state {
175 : struct smbd_smb2_request *smb2req;
176 : struct smb_request *smbreq;
177 : files_struct *fsp;
178 : bool write_through;
179 : uint32_t in_length;
180 : uint64_t in_offset;
181 : uint32_t out_count;
182 : };
183 :
184 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq);
185 :
186 38957 : static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
187 : ssize_t nwritten, int err,
188 : bool do_sync)
189 : {
190 : NTSTATUS status;
191 38957 : struct smbd_smb2_write_state *state = tevent_req_data(req,
192 : struct smbd_smb2_write_state);
193 38957 : files_struct *fsp = state->fsp;
194 :
195 38957 : if (nwritten == -1) {
196 2230 : if (err == EOVERFLOW &&
197 0 : is_ntfs_stream_smb_fname(fsp->fsp_name)) {
198 0 : status = NT_STATUS_FILE_SYSTEM_LIMITATION;
199 : } else {
200 2230 : status = map_nt_error_from_unix(err);
201 : }
202 :
203 2230 : DEBUG(2, ("smb2_write failed: %s, file %s, "
204 : "length=%lu offset=%lu nwritten=-1: %s\n",
205 : fsp_fnum_dbg(fsp),
206 : fsp_str_dbg(fsp),
207 : (unsigned long)state->in_length,
208 : (unsigned long)state->in_offset,
209 : nt_errstr(status)));
210 :
211 2230 : return status;
212 : }
213 :
214 36727 : DEBUG(3,("smb2: %s, file %s, "
215 : "length=%lu offset=%lu wrote=%lu\n",
216 : fsp_fnum_dbg(fsp),
217 : fsp_str_dbg(fsp),
218 : (unsigned long)state->in_length,
219 : (unsigned long)state->in_offset,
220 : (unsigned long)nwritten));
221 :
222 36727 : if ((nwritten == 0) && (state->in_length != 0)) {
223 0 : DEBUG(5,("smb2: write [%s] disk full\n",
224 : fsp_str_dbg(fsp)));
225 0 : return NT_STATUS_DISK_FULL;
226 : }
227 :
228 36727 : if (do_sync) {
229 878 : status = sync_file(fsp->conn, fsp, state->write_through);
230 878 : if (!NT_STATUS_IS_OK(status)) {
231 0 : DEBUG(5,("smb2: sync_file for %s returned %s\n",
232 : fsp_str_dbg(fsp),
233 : nt_errstr(status)));
234 0 : return status;
235 : }
236 : }
237 :
238 36727 : state->out_count = nwritten;
239 :
240 36727 : return NT_STATUS_OK;
241 : }
242 :
243 3088 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
244 : {
245 3088 : return smb2_write_complete_internal(req, nwritten, err, true);
246 : }
247 :
248 35869 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
249 : int err)
250 : {
251 35869 : return smb2_write_complete_internal(req, nwritten, err, false);
252 : }
253 :
254 :
255 0 : static bool smbd_smb2_write_cancel(struct tevent_req *req)
256 : {
257 0 : struct smbd_smb2_write_state *state =
258 0 : tevent_req_data(req,
259 : struct smbd_smb2_write_state);
260 :
261 0 : return cancel_smb2_aio(state->smbreq);
262 : }
263 :
264 46368 : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
265 : struct tevent_context *ev,
266 : struct smbd_smb2_request *smb2req,
267 : struct files_struct *fsp,
268 : DATA_BLOB in_data,
269 : uint64_t in_offset,
270 : uint32_t in_flags)
271 : {
272 : NTSTATUS status;
273 46368 : struct tevent_req *req = NULL;
274 46368 : struct smbd_smb2_write_state *state = NULL;
275 46368 : struct smb_request *smbreq = NULL;
276 46368 : connection_struct *conn = smb2req->tcon->compat;
277 : ssize_t nwritten;
278 : struct lock_struct lock;
279 :
280 46368 : req = tevent_req_create(mem_ctx, &state,
281 : struct smbd_smb2_write_state);
282 46368 : if (req == NULL) {
283 0 : return NULL;
284 : }
285 46368 : state->smb2req = smb2req;
286 46368 : if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
287 41650 : if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
288 2 : state->write_through = true;
289 : }
290 : }
291 46368 : if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
292 2 : state->write_through = true;
293 : }
294 46368 : state->in_length = in_data.length;
295 46368 : state->in_offset = in_offset;
296 46368 : state->out_count = 0;
297 :
298 46368 : DEBUG(10,("smbd_smb2_write: %s - %s\n",
299 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
300 :
301 46368 : smbreq = smbd_smb2_fake_smb_request(smb2req);
302 46368 : if (tevent_req_nomem(smbreq, req)) {
303 0 : return tevent_req_post(req, ev);
304 : }
305 46368 : state->smbreq = smbreq;
306 :
307 46368 : state->fsp = fsp;
308 :
309 46368 : if (IS_IPC(smbreq->conn)) {
310 7229 : struct tevent_req *subreq = NULL;
311 :
312 7229 : if (!fsp_is_np(fsp)) {
313 0 : tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
314 0 : return tevent_req_post(req, ev);
315 : }
316 :
317 12226 : subreq = np_write_send(state, ev,
318 : fsp->fake_file_handle,
319 7182 : in_data.data,
320 : in_data.length);
321 7229 : if (tevent_req_nomem(subreq, req)) {
322 0 : return tevent_req_post(req, ev);
323 : }
324 7229 : tevent_req_set_callback(subreq,
325 : smbd_smb2_write_pipe_done,
326 : req);
327 7229 : return req;
328 : }
329 :
330 39139 : if (!CHECK_WRITE(fsp)) {
331 166 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
332 166 : return tevent_req_post(req, ev);
333 : }
334 :
335 : /* Try and do an asynchronous write. */
336 38973 : status = schedule_aio_smb2_write(conn,
337 : smbreq,
338 : fsp,
339 : in_offset,
340 : in_data,
341 38973 : state->write_through);
342 :
343 38973 : if (NT_STATUS_IS_OK(status)) {
344 : /*
345 : * Doing an async write, allow this
346 : * request to be canceled
347 : */
348 35873 : tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
349 35873 : return req;
350 : }
351 :
352 3100 : if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
353 : /* Real error in setting up aio. Fail. */
354 12 : tevent_req_nterror(req, status);
355 12 : return tevent_req_post(req, ev);
356 : }
357 :
358 : /* Fallback to synchronous. */
359 6152 : init_strict_lock_struct(fsp,
360 3088 : fsp->op->global->open_persistent_id,
361 : in_offset,
362 : in_data.length,
363 : WRITE_LOCK,
364 : &lock);
365 :
366 3088 : if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &lock)) {
367 0 : tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
368 0 : return tevent_req_post(req, ev);
369 : }
370 :
371 : /*
372 : * Note: in_data.data is NULL for the recvfile case.
373 : */
374 6152 : nwritten = write_file(smbreq, fsp,
375 3088 : (const char *)in_data.data,
376 : in_offset,
377 : in_data.length);
378 :
379 3088 : status = smb2_write_complete(req, nwritten, errno);
380 :
381 3088 : DEBUG(10,("smb2: write on "
382 : "file %s, offset %.0f, requested %u, written = %u\n",
383 : fsp_str_dbg(fsp),
384 : (double)in_offset,
385 : (unsigned int)in_data.length,
386 : (unsigned int)nwritten ));
387 :
388 3088 : if (!NT_STATUS_IS_OK(status)) {
389 2210 : tevent_req_nterror(req, status);
390 : } else {
391 : /* Success. */
392 878 : tevent_req_done(req);
393 : }
394 :
395 3088 : return tevent_req_post(req, ev);
396 : }
397 :
398 7229 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
399 : {
400 7229 : struct tevent_req *req = tevent_req_callback_data(subreq,
401 : struct tevent_req);
402 7229 : struct smbd_smb2_write_state *state = tevent_req_data(req,
403 : struct smbd_smb2_write_state);
404 : NTSTATUS status;
405 7229 : ssize_t nwritten = -1;
406 :
407 7229 : status = np_write_recv(subreq, &nwritten);
408 7229 : TALLOC_FREE(subreq);
409 7229 : if (!NT_STATUS_IS_OK(status)) {
410 0 : NTSTATUS old = status;
411 0 : status = nt_status_np_pipe(old);
412 0 : tevent_req_nterror(req, status);
413 0 : return;
414 : }
415 :
416 7229 : if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
417 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
418 0 : return;
419 : }
420 :
421 7229 : state->out_count = nwritten;
422 :
423 7229 : tevent_req_done(req);
424 : }
425 :
426 46364 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
427 : uint32_t *out_count)
428 : {
429 : NTSTATUS status;
430 46364 : struct smbd_smb2_write_state *state = tevent_req_data(req,
431 : struct smbd_smb2_write_state);
432 :
433 46364 : if (tevent_req_is_nterror(req, &status)) {
434 2408 : tevent_req_received(req);
435 2408 : return status;
436 : }
437 :
438 43956 : *out_count = state->out_count;
439 :
440 43956 : tevent_req_received(req);
441 43956 : return NT_STATUS_OK;
442 : }
|