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 6348 : NTSTATUS smbd_smb2_request_process_write(struct smbd_smb2_request *req)
43 : {
44 6348 : 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 6348 : size_t in_dyn_len = 0;
56 6348 : uint8_t *in_dyn_ptr = NULL;
57 : struct tevent_req *subreq;
58 :
59 6348 : status = smbd_smb2_request_verify_sizes(req, 0x31);
60 6348 : if (!NT_STATUS_IS_OK(status)) {
61 0 : return smbd_smb2_request_error(req, status);
62 : }
63 6348 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
64 :
65 6348 : in_data_offset = SVAL(inbody, 0x02);
66 6348 : in_data_length = IVAL(inbody, 0x04);
67 6348 : in_offset = BVAL(inbody, 0x08);
68 6348 : in_file_id_persistent = BVAL(inbody, 0x10);
69 6348 : in_file_id_volatile = BVAL(inbody, 0x18);
70 6348 : in_flags = IVAL(inbody, 0x2C);
71 :
72 6348 : 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 6348 : 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 6348 : in_dyn_ptr = SMBD_SMB2_IN_DYN_PTR(req);
81 6348 : in_dyn_len = SMBD_SMB2_IN_DYN_LEN(req);
82 : }
83 :
84 6348 : 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 6348 : 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 6348 : in_data_buffer.data = in_dyn_ptr;
100 6348 : in_data_buffer.length = in_data_length;
101 :
102 6348 : status = smbd_smb2_request_verify_creditcharge(req, in_data_length);
103 6348 : if (!NT_STATUS_IS_OK(status)) {
104 0 : return smbd_smb2_request_error(req, status);
105 : }
106 :
107 6348 : in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
108 6348 : if (in_fsp == NULL) {
109 0 : return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
110 : }
111 :
112 6348 : 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 6348 : if (subreq == NULL) {
118 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
119 : }
120 6348 : tevent_req_set_callback(subreq, smbd_smb2_request_write_done, req);
121 :
122 6348 : return smbd_smb2_request_pending_queue(req, subreq, 500);
123 : }
124 :
125 6348 : static void smbd_smb2_request_write_done(struct tevent_req *subreq)
126 : {
127 6348 : struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
128 : struct smbd_smb2_request);
129 : DATA_BLOB outbody;
130 : DATA_BLOB outdyn;
131 6348 : uint32_t out_count = 0;
132 : NTSTATUS status;
133 : NTSTATUS error; /* transport error */
134 :
135 6348 : status = smbd_smb2_write_recv(subreq, &out_count);
136 6348 : TALLOC_FREE(subreq);
137 6348 : if (!NT_STATUS_IS_OK(status)) {
138 0 : error = smbd_smb2_request_error(req, status);
139 0 : if (!NT_STATUS_IS_OK(error)) {
140 0 : smbd_server_connection_terminate(req->xconn,
141 : nt_errstr(error));
142 0 : return;
143 : }
144 0 : return;
145 : }
146 :
147 6348 : outbody = smbd_smb2_generate_outbody(req, 0x10);
148 6348 : 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 6348 : SSVAL(outbody.data, 0x00, 0x10 + 1); /* struct size */
159 6348 : SSVAL(outbody.data, 0x02, 0); /* reserved */
160 6348 : SIVAL(outbody.data, 0x04, out_count); /* count */
161 6348 : SIVAL(outbody.data, 0x08, 0); /* remaining */
162 6348 : SSVAL(outbody.data, 0x0C, 0); /* write channel info offset */
163 6348 : SSVAL(outbody.data, 0x0E, 0); /* write channel info length */
164 :
165 6348 : outdyn = data_blob_const(NULL, 0);
166 :
167 6348 : error = smbd_smb2_request_done(req, outbody, &outdyn);
168 6348 : 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 447 : 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 447 : struct smbd_smb2_write_state *state = tevent_req_data(req,
192 : struct smbd_smb2_write_state);
193 447 : files_struct *fsp = state->fsp;
194 :
195 447 : if (nwritten == -1) {
196 0 : if (err == EOVERFLOW && fsp_is_alternate_stream(fsp)) {
197 0 : status = NT_STATUS_FILE_SYSTEM_LIMITATION;
198 : } else {
199 0 : status = map_nt_error_from_unix(err);
200 : }
201 :
202 0 : 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 0 : return status;
211 : }
212 :
213 447 : 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 447 : 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 447 : if (do_sync) {
228 24 : status = sync_file(fsp->conn, fsp, state->write_through);
229 24 : 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 447 : state->out_count = nwritten;
238 :
239 447 : return NT_STATUS_OK;
240 : }
241 :
242 24 : NTSTATUS smb2_write_complete(struct tevent_req *req, ssize_t nwritten, int err)
243 : {
244 24 : return smb2_write_complete_internal(req, nwritten, err, true);
245 : }
246 :
247 423 : NTSTATUS smb2_write_complete_nosync(struct tevent_req *req, ssize_t nwritten,
248 : int err)
249 : {
250 423 : 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 6348 : 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 : NTSTATUS status;
272 6348 : struct tevent_req *req = NULL;
273 6348 : struct smbd_smb2_write_state *state = NULL;
274 6348 : struct smb_request *smbreq = NULL;
275 6348 : connection_struct *conn = smb2req->tcon->compat;
276 : ssize_t nwritten;
277 : struct lock_struct lock;
278 :
279 6348 : req = tevent_req_create(mem_ctx, &state,
280 : struct smbd_smb2_write_state);
281 6348 : if (req == NULL) {
282 0 : return NULL;
283 : }
284 6348 : state->smb2req = smb2req;
285 6348 : if (smb2req->xconn->protocol >= PROTOCOL_SMB3_02) {
286 1698 : if (in_flags & SMB2_WRITEFLAG_WRITE_UNBUFFERED) {
287 0 : state->write_through = true;
288 : }
289 : }
290 6348 : if (in_flags & SMB2_WRITEFLAG_WRITE_THROUGH) {
291 0 : state->write_through = true;
292 : }
293 6348 : state->in_length = in_data.length;
294 6348 : state->in_offset = in_offset;
295 6348 : state->out_count = 0;
296 :
297 6348 : DEBUG(10,("smbd_smb2_write: %s - %s\n",
298 : fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
299 :
300 6348 : smbreq = smbd_smb2_fake_smb_request(smb2req);
301 6348 : if (tevent_req_nomem(smbreq, req)) {
302 0 : return tevent_req_post(req, ev);
303 : }
304 6348 : state->smbreq = smbreq;
305 :
306 6348 : state->fsp = fsp;
307 :
308 6348 : if (IS_IPC(smbreq->conn)) {
309 5901 : struct tevent_req *subreq = NULL;
310 :
311 5901 : if (!fsp_is_np(fsp)) {
312 0 : tevent_req_nterror(req, NT_STATUS_FILE_CLOSED);
313 0 : return tevent_req_post(req, ev);
314 : }
315 :
316 10000 : subreq = np_write_send(state, ev,
317 : fsp->fake_file_handle,
318 5901 : in_data.data,
319 : in_data.length);
320 5901 : if (tevent_req_nomem(subreq, req)) {
321 0 : return tevent_req_post(req, ev);
322 : }
323 5901 : tevent_req_set_callback(subreq,
324 : smbd_smb2_write_pipe_done,
325 : req);
326 5901 : return req;
327 : }
328 :
329 447 : if (!CHECK_WRITE(fsp)) {
330 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
331 0 : return tevent_req_post(req, ev);
332 : }
333 :
334 : /* Try and do an asynchronous write. */
335 447 : status = schedule_aio_smb2_write(conn,
336 : smbreq,
337 : fsp,
338 : in_offset,
339 : in_data,
340 447 : state->write_through);
341 :
342 447 : if (NT_STATUS_IS_OK(status)) {
343 : /*
344 : * Doing an async write, allow this
345 : * request to be canceled
346 : */
347 423 : tevent_req_set_cancel_fn(req, smbd_smb2_write_cancel);
348 423 : return req;
349 : }
350 :
351 24 : if (!NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) {
352 : /* Real error in setting up aio. Fail. */
353 0 : tevent_req_nterror(req, status);
354 0 : return tevent_req_post(req, ev);
355 : }
356 :
357 : /* Fallback to synchronous. */
358 36 : init_strict_lock_struct(fsp,
359 24 : fsp->op->global->open_persistent_id,
360 : in_offset,
361 : in_data.length,
362 : WRITE_LOCK,
363 : lp_posix_cifsu_locktype(fsp),
364 : &lock);
365 :
366 24 : 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 36 : nwritten = write_file(smbreq, fsp,
375 24 : (const char *)in_data.data,
376 : in_offset,
377 : in_data.length);
378 :
379 24 : status = smb2_write_complete(req, nwritten, errno);
380 :
381 24 : 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 24 : if (!NT_STATUS_IS_OK(status)) {
389 0 : tevent_req_nterror(req, status);
390 : } else {
391 : /* Success. */
392 24 : tevent_req_done(req);
393 : }
394 :
395 24 : return tevent_req_post(req, ev);
396 : }
397 :
398 5901 : static void smbd_smb2_write_pipe_done(struct tevent_req *subreq)
399 : {
400 5901 : struct tevent_req *req = tevent_req_callback_data(subreq,
401 : struct tevent_req);
402 5901 : struct smbd_smb2_write_state *state = tevent_req_data(req,
403 : struct smbd_smb2_write_state);
404 : NTSTATUS status;
405 5901 : ssize_t nwritten = -1;
406 :
407 5901 : status = np_write_recv(subreq, &nwritten);
408 5901 : TALLOC_FREE(subreq);
409 5901 : 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 5901 : 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 5901 : state->out_count = nwritten;
422 :
423 5901 : tevent_req_done(req);
424 : }
425 :
426 6348 : static NTSTATUS smbd_smb2_write_recv(struct tevent_req *req,
427 : uint32_t *out_count)
428 : {
429 : NTSTATUS status;
430 6348 : struct smbd_smb2_write_state *state = tevent_req_data(req,
431 : struct smbd_smb2_write_state);
432 :
433 6348 : if (tevent_req_is_nterror(req, &status)) {
434 0 : tevent_req_received(req);
435 0 : return status;
436 : }
437 :
438 6348 : *out_count = state->out_count;
439 :
440 6348 : tevent_req_received(req);
441 6348 : return NT_STATUS_OK;
442 : }
|