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 63724 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
43 : {
44 63724 : struct smbXsrv_connection *xconn = req->xconn;
45 47 : NTSTATUS status;
46 47 : const uint8_t *inbody;
47 47 : uint16_t in_data_offset;
48 47 : uint32_t in_data_length;
49 47 : DATA_BLOB in_data_buffer;
50 47 : uint64_t in_offset;
51 47 : uint64_t in_file_id_persistent;
52 47 : uint64_t in_file_id_volatile;
53 47 : struct files_struct *in_fsp;
54 47 : uint32_t in_flags;
55 63724 : size_t in_dyn_len = 0;
56 63724 : uint8_t *in_dyn_ptr = NULL;
57 47 : struct tevent_req *subreq;
58 :
59 63724 : status = smbd_smb2_request_verify_sizes(req, 0x31);
60 63724 : if (!NT_STATUS_IS_OK(status)) {
61 0 : return smbd_smb2_request_error(req, status);
62 : }
63 63724 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
64 :
65 63724 : in_data_offset = SVAL(inbody, 0x02);
66 63724 : in_data_length = IVAL(inbody, 0x04);
67 63724 : in_offset = BVAL(inbody, 0x08);
68 63724 : in_file_id_persistent = BVAL(inbody, 0x10);
69 63724 : in_file_id_volatile = BVAL(inbody, 0x18);
70 63724 : in_flags = IVAL(inbody, 0x2C);
71 :
72 63724 : 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 63724 : 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 63724 : in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
81 63724 : in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
82 : }
83 :
84 63724 : 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 63724 : 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 63724 : in_data_buffer.data = in_dyn_ptr;
100 63724 : in_data_buffer.length = in_data_length;
101 :
102 63724 : status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
103 63724 : if (!NT_STATUS_IS_OK(status)) {
104 0 : return smbd_smb2_request_error(req, status);
105 : }
106 :
107 63724 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
108 63724 : if (in_fsp == NULL) {
109 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
110 : }
111 :
112 63724 : 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 63724 : if (subreq == NULL) {
118 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
119 : }
120 63724 : tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
121 :
122 63724 : return smbd_smb2_request_pending_queue(req, subreq, 500);
123 : }
124 :
125 63720 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
126 : {
127 63720 : struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
128 : struct smbd_smb2_request);
129 47 : DATA_BLOB outbody;
130 47 : DATA_BLOB outdyn;
131 63720 : uint32_t out_count = 0;
132 47 : NTSTATUS status;
133 47 : NTSTATUS error; /* transport error */
134 :
135 63720 : status = smbd_smb2_write_recv(subreq, &out_count);
136 63720 : TALLOC_FREE(subreq);
137 63720 : if (!NT_STATUS_IS_OK(status)) {
138 2424 : error = smbd_smb2_request_error(req, status);
139 2424 : if (!NT_STATUS_IS_OK(error)) {
140 0 : smbd_server_connection_terminate(req->xconn,
141 : nt_errstr(error));
142 2424 : return;
143 : }
144 2424 : return;
145 : }
146 :
147 61296 : outbody = smbd_smb2_generate_outbody(req, 0x10);
148 61296 : 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 61296 : SSVAL(outbody.data, 0x00, 0x10 + 1); /* struct size */
159 61296 : SSVAL(outbody.data, 0x02, 0); /* reserved */
160 61296 : SIVAL(outbody.data, 0x04, out_count); /* count */
161 61296 : SIVAL(outbody.data, 0x08, 0); /* remaining */
162 61296 : SSVAL(outbody.data, 0x0C, 0); /* write channel info offset */
163 61296 : SSVAL(outbody.data, 0x0E, 0); /* write channel info length */
164 :
165 61296 : outdyn = data_blob_const(NULL, 0);
166 :
167 61296 : error = smbd_smb2_request_done(req, outbody, &outdyn);
168 61296 : 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 56405 : static NTSTATUS smb2_write_complete_internal(struct tevent_req *req,
187 : ssize_t nwritten, int err,
188 : bool do_sync)
189 : {
190 0 : NTSTATUS status;
191 56405 : struct smbd_smb2_write_state *state = tevent_req_data(req,
192 : struct smbd_smb2_write_state);
193 56405 : files_struct *fsp = state->fsp;
194 :
195 56405 : if (nwritten == -1) {
196 2246 : if (err == EOVERFLOW && fsp_is_alternate_stream(fsp)) {
197 0 : status = NT_STATUS_FILE_SYSTEM_LIMITATION;
198 : } else {
199 2246 : status = map_nt_error_from_unix(err);
200 : }
201 :
202 2246 : DEBUG(2, ("smb2_write failed: %s, file %s, "
203 : "length=%lu offset=%lu nwritten=-1: %s\n",
204 : fsp_fnum_dbg(fsp),
205 : fsp_str_dbg(fsp),
206 : (unsigned long)state->in_length,
207 : (unsigned long)state->in_offset,
208 : nt_errstr(status)));
209 :
210 2246 : return status;
211 : }
212 :
213 54159 : DEBUG(3,("smb2: %s, file %s, "
214 : "length=%lu offset=%lu wrote=%lu\n",
215 : fsp_fnum_dbg(fsp),
216 : fsp_str_dbg(fsp),
217 : (unsigned long)state->in_length,
218 : (unsigned long)state->in_offset,
219 : (unsigned long)nwritten));
220 :
221 54159 : if ((nwritten == 0) && (state->in_length != 0)) {
222 0 : DEBUG(5,("smb2: write [%s] disk full\n",
223 : fsp_str_dbg(fsp)));
224 0 : return NT_STATUS_DISK_FULL;
225 : }
226 :
227 54159 : if (do_sync) {
228 926 : status = sync_file(fsp->conn, fsp, state->write_through);
229 926 : if (!NT_STATUS_IS_OK(status)) {
230 0 : DEBUG(5,("smb2: sync_file for %s returned %s\n",
231 : fsp_str_dbg(fsp),
232 : nt_errstr(status)));
233 0 : return status;
234 : }
235 : }
236 :
237 54159 : state->out_count = nwritten;
238 :
239 54159 : return NT_STATUS_OK;
240 : }
241 :
242 3142 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
243 : {
244 3142 : return smb2_write_complete_internal(req, nwritten, err, true);
245 : }
246 :
247 53263 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
248 : int err)
249 : {
250 53263 : return smb2_write_complete_internal(req, nwritten, err, false);
251 : }
252 :
253 :
254 0 : static bool smbd_smb2_write_cancel(struct tevent_req *req)
255 : {
256 0 : struct smbd_smb2_write_state *state =
257 0 : tevent_req_data(req,
258 : struct smbd_smb2_write_state);
259 :
260 0 : return cancel_smb2_aio(state->smbreq);
261 : }
262 :
263 63724 : static struct tevent_req *smbd_smb2_write_send(TALLOC_CTX *mem_ctx,
264 : struct tevent_context *ev,
265 : struct smbd_smb2_request *smb2req,
266 : struct files_struct *fsp,
267 : DATA_BLOB in_data,
268 : uint64_t in_offset,
269 : uint32_t in_flags)
270 : {
271 47 : NTSTATUS status;
272 63724 : struct tevent_req *req = NULL;
273 63724 : struct smbd_smb2_write_state *state = NULL;
274 63724 : struct smb_request *smbreq = NULL;
275 63724 : connection_struct *conn = smb2req->tcon->compat;
276 47 : ssize_t nwritten;
277 47 : struct lock_struct lock;
278 :
279 63724 : req = tevent_req_create(mem_ctx, &state,
280 : struct smbd_smb2_write_state);
281 63724 : if (req == NULL) {
282 0 : return NULL;
283 : }
284 63724 : state->smb2req = smb2req;
285 63724 : if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
286 58916 : if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
287 0 : state->write_through = true;
288 : }
289 : }
290 63724 : if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
291 4 : state->write_through = true;
292 : }
293 63724 : state->in_length = in_data.length;
294 63724 : state->in_offset = in_offset;
295 63724 : state->out_count = 0;
296 :
297 63724 : DEBUG(10,("smbd_smb2_write: %s - %s\n",
298 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
299 :
300 63724 : smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
301 63724 : if (tevent_req_nomem(smbreq, req)) {
302 0 : return tevent_req_post(req, ev);
303 : }
304 63724 : state->smbreq = smbreq;
305 :
306 63724 : state->fsp = fsp;
307 :
308 63724 : if (IS_IPC(smbreq->conn)) {
309 7137 : struct tevent_req *subreq = NULL;
310 47 : bool ok;
311 :
312 7137 : 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 7137 : subreq = np_write_send(state, ev,
318 : fsp->fake_file_handle,
319 7090 : in_data.data,
320 : in_data.length);
321 7137 : if (tevent_req_nomem(subreq, req)) {
322 0 : return tevent_req_post(req, ev);
323 : }
324 7137 : tevent_req_set_callback(subreq,
325 : smbd_smb2_write_pipe_done,
326 : req);
327 :
328 : /*
329 : * Make sure we mark the fsp as having outstanding async
330 : * activity so we don't crash on shutdown close.
331 : */
332 :
333 7137 : ok = aio_add_req_to_fsp(fsp, req);
334 7137 : if (!ok) {
335 0 : tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
336 0 : return tevent_req_post(req, ev);
337 : }
338 :
339 7090 : return req;
340 : }
341 :
342 56587 : if (!CHECK_WRITE(fsp)) {
343 166 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
344 166 : return tevent_req_post(req, ev);
345 : }
346 :
347 : /* Try and do an asynchronous write. */
348 56421 : status = schedule_aio_smb2_write(conn,
349 : smbreq,
350 : fsp,
351 : in_offset,
352 : in_data,
353 56421 : state->write_through);
354 :
355 56421 : if (NT_STATUS_IS_OK(status)) {
356 : /*
357 : * Doing an async write, allow this
358 : * request to be canceled
359 : */
360 53267 : tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
361 53267 : return req;
362 : }
363 :
364 3154 : if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
365 : /* Real error in setting up aio. Fail. */
366 12 : tevent_req_nterror(req, status);
367 12 : return tevent_req_post(req, ev);
368 : }
369 :
370 : /* Fallback to synchronous. */
371 3142 : init_strict_lock_struct(fsp,
372 3142 : fsp->op->global->open_persistent_id,
373 : in_offset,
374 : in_data.length,
375 : WRITE_LOCK,
376 : lp_posix_cifsu_locktype(fsp),
377 : &lock);
378 :
379 3142 : if (!SMB_VFS_STRICT_LOCK_CHECK(conn, fsp, &lock)) {
380 0 : tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
381 0 : return tevent_req_post(req, ev);
382 : }
383 :
384 : /*
385 : * Note: in_data.data is NULL for the recvfile case.
386 : */
387 3142 : nwritten = write_file(smbreq, fsp,
388 3142 : (const char *)in_data.data,
389 : in_offset,
390 : in_data.length);
391 :
392 3142 : status = smb2_write_complete(req, nwritten, errno);
393 :
394 3142 : DEBUG(10,("smb2: write on "
395 : "file %s, offset %.0f, requested %u, written = %u\n",
396 : fsp_str_dbg(fsp),
397 : (double)in_offset,
398 : (unsigned int)in_data.length,
399 : (unsigned int)nwritten ));
400 :
401 3142 : if (!NT_STATUS_IS_OK(status)) {
402 2216 : tevent_req_nterror(req, status);
403 : } else {
404 : /* Success. */
405 926 : tevent_req_done(req);
406 : }
407 :
408 3142 : return tevent_req_post(req, ev);
409 : }
410 :
411 7137 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
412 : {
413 7137 : struct tevent_req *req = tevent_req_callback_data(subreq,
414 : struct tevent_req);
415 7137 : struct smbd_smb2_write_state *state = tevent_req_data(req,
416 : struct smbd_smb2_write_state);
417 47 : NTSTATUS status;
418 7137 : ssize_t nwritten = -1;
419 :
420 7137 : status = np_write_recv(subreq, &nwritten);
421 7137 : TALLOC_FREE(subreq);
422 7137 : if (!NT_STATUS_IS_OK(status)) {
423 0 : NTSTATUS old = status;
424 0 : status = nt_status_np_pipe(old);
425 0 : tevent_req_nterror(req, status);
426 0 : return;
427 : }
428 :
429 7137 : if ((nwritten == 0 && state->in_length != 0) || (nwritten < 0)) {
430 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
431 0 : return;
432 : }
433 :
434 7137 : state->out_count = nwritten;
435 :
436 7137 : tevent_req_done(req);
437 : }
438 :
439 63720 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
440 : uint32_t *out_count)
441 : {
442 47 : NTSTATUS status;
443 63720 : struct smbd_smb2_write_state *state = tevent_req_data(req,
444 : struct smbd_smb2_write_state);
445 :
446 63720 : if (tevent_req_is_nterror(req, &status)) {
447 2424 : tevent_req_received(req);
448 2424 : return status;
449 : }
450 :
451 61296 : *out_count = state->out_count;
452 :
453 61296 : tevent_req_received(req);
454 61296 : return NT_STATUS_OK;
455 : }
|