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/globals.h" 23#include "../libcli/smb/smb_common.h" 24 25static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint32_t in_smbpid, 29 uint64_t in_file_id_volatile, 30 uint32_t in_length, 31 uint64_t in_offset, 32 uint32_t in_minimum, 33 uint32_t in_remaining); 34static NTSTATUS smbd_smb2_read_recv(struct tevent_req *req, 35 TALLOC_CTX *mem_ctx, 36 DATA_BLOB *out_data, 37 uint32_t *out_remaining); 38 39static void smbd_smb2_request_read_done(struct tevent_req *subreq); 40NTSTATUS smbd_smb2_request_process_read(struct smbd_smb2_request *req) 41{ 42 const uint8_t *inhdr; 43 const uint8_t *inbody; 44 int i = req->current_idx; 45 size_t expected_body_size = 0x31; 46 size_t body_size; 47 uint32_t in_smbpid; 48 uint32_t in_length; 49 uint64_t in_offset; 50 uint64_t in_file_id_persistent; 51 uint64_t in_file_id_volatile; 52 uint32_t in_minimum_count; 53 uint32_t in_remaining_bytes; 54 struct tevent_req *subreq; 55 56 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 57 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 58 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 59 } 60 61 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 62 63 body_size = SVAL(inbody, 0x00); 64 if (body_size != expected_body_size) { 65 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 66 } 67 68 in_smbpid = IVAL(inhdr, SMB2_HDR_PID); 69 70 in_length = IVAL(inbody, 0x04); 71 in_offset = BVAL(inbody, 0x08); 72 in_file_id_persistent = BVAL(inbody, 0x10); 73 in_file_id_volatile = BVAL(inbody, 0x18); 74 in_minimum_count = IVAL(inbody, 0x20); 75 in_remaining_bytes = IVAL(inbody, 0x28); 76 77 /* check the max read size */ 78 if (in_length > 0x00010000) { 79 DEBUG(0,("here:%s: 0x%08X: 0x%08X\n", 80 __location__, in_length, 0x00010000)); 81 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 82 } 83 84 if (req->compat_chain_fsp) { 85 /* skip check */ 86 } else if (in_file_id_persistent != 0) { 87 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 88 } 89 90 subreq = smbd_smb2_read_send(req, 91 req->sconn->smb2.event_ctx, 92 req, 93 in_smbpid, 94 in_file_id_volatile, 95 in_length, 96 in_offset, 97 in_minimum_count, 98 in_remaining_bytes); 99 if (subreq == NULL) { 100 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 101 } 102 tevent_req_set_callback(subreq, smbd_smb2_request_read_done, req); 103 104 return smbd_smb2_request_pending_queue(req, subreq); 105} 106 107static void smbd_smb2_request_read_done(struct tevent_req *subreq) 108{ 109 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 110 struct smbd_smb2_request); 111 int i = req->current_idx; 112 uint8_t *outhdr; 113 DATA_BLOB outbody; 114 DATA_BLOB outdyn; 115 uint8_t out_data_offset; 116 DATA_BLOB out_data_buffer = data_blob_null; 117 uint32_t out_data_remaining = 0; 118 NTSTATUS status; 119 NTSTATUS error; /* transport error */ 120 121 status = smbd_smb2_read_recv(subreq, 122 req, 123 &out_data_buffer, 124 &out_data_remaining); 125 TALLOC_FREE(subreq); 126 if (!NT_STATUS_IS_OK(status)) { 127 error = smbd_smb2_request_error(req, status); 128 if (!NT_STATUS_IS_OK(error)) { 129 smbd_server_connection_terminate(req->sconn, 130 nt_errstr(error)); 131 return; 132 } 133 return; 134 } 135 136 out_data_offset = SMB2_HDR_BODY + 0x10; 137 138 outhdr = (uint8_t *)req->out.vector[i].iov_base; 139 140 outbody = data_blob_talloc(req->out.vector, NULL, 0x10); 141 if (outbody.data == NULL) { 142 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 143 if (!NT_STATUS_IS_OK(error)) { 144 smbd_server_connection_terminate(req->sconn, 145 nt_errstr(error)); 146 return; 147 } 148 return; 149 } 150 151 SSVAL(outbody.data, 0x00, 0x10 + 1); /* struct size */ 152 SCVAL(outbody.data, 0x02, 153 out_data_offset); /* data offset */ 154 SCVAL(outbody.data, 0x03, 0); /* reserved */ 155 SIVAL(outbody.data, 0x04, 156 out_data_buffer.length); /* data length */ 157 SIVAL(outbody.data, 0x08, 158 out_data_remaining); /* data remaining */ 159 SIVAL(outbody.data, 0x0C, 0); /* reserved */ 160 161 outdyn = out_data_buffer; 162 163 error = smbd_smb2_request_done(req, outbody, &outdyn); 164 if (!NT_STATUS_IS_OK(error)) { 165 smbd_server_connection_terminate(req->sconn, 166 nt_errstr(error)); 167 return; 168 } 169} 170 171struct smbd_smb2_read_state { 172 struct smbd_smb2_request *smb2req; 173 DATA_BLOB out_data; 174 uint32_t out_remaining; 175}; 176 177static void smbd_smb2_read_pipe_done(struct tevent_req *subreq); 178 179static struct tevent_req *smbd_smb2_read_send(TALLOC_CTX *mem_ctx, 180 struct tevent_context *ev, 181 struct smbd_smb2_request *smb2req, 182 uint32_t in_smbpid, 183 uint64_t in_file_id_volatile, 184 uint32_t in_length, 185 uint64_t in_offset, 186 uint32_t in_minimum, 187 uint32_t in_remaining) 188{ 189 struct tevent_req *req; 190 struct smbd_smb2_read_state *state; 191 struct smb_request *smbreq; 192 connection_struct *conn = smb2req->tcon->compat_conn; 193 files_struct *fsp; 194 ssize_t nread = -1; 195 struct lock_struct lock; 196 197 req = tevent_req_create(mem_ctx, &state, 198 struct smbd_smb2_read_state); 199 if (req == NULL) { 200 return NULL; 201 } 202 state->smb2req = smb2req; 203 state->out_data = data_blob_null; 204 state->out_remaining = 0; 205 206 DEBUG(10,("smbd_smb2_read: file_id[0x%016llX]\n", 207 (unsigned long long)in_file_id_volatile)); 208 209 smbreq = smbd_smb2_fake_smb_request(smb2req); 210 if (tevent_req_nomem(smbreq, req)) { 211 return tevent_req_post(req, ev); 212 } 213 214 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 215 if (fsp == NULL) { 216 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 217 return tevent_req_post(req, ev); 218 } 219 if (conn != fsp->conn) { 220 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 221 return tevent_req_post(req, ev); 222 } 223 if (smb2req->session->vuid != fsp->vuid) { 224 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 225 return tevent_req_post(req, ev); 226 } 227 228 state->out_data = data_blob_talloc(state, NULL, in_length); 229 if (in_length > 0 && tevent_req_nomem(state->out_data.data, req)) { 230 return tevent_req_post(req, ev); 231 } 232 233 if (IS_IPC(smbreq->conn)) { 234 struct tevent_req *subreq; 235 236 if (!fsp_is_np(fsp)) { 237 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 238 return tevent_req_post(req, ev); 239 } 240 241 subreq = np_read_send(state, smbd_event_context(), 242 fsp->fake_file_handle, 243 state->out_data.data, 244 state->out_data.length); 245 if (tevent_req_nomem(subreq, req)) { 246 return tevent_req_post(req, ev); 247 } 248 tevent_req_set_callback(subreq, 249 smbd_smb2_read_pipe_done, 250 req); 251 return req; 252 } 253 254 if (!CHECK_READ(fsp, smbreq)) { 255 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); 256 return tevent_req_post(req, ev); 257 } 258 259 init_strict_lock_struct(fsp, 260 in_smbpid, 261 in_offset, 262 in_length, 263 READ_LOCK, 264 &lock); 265 266 if (!SMB_VFS_STRICT_LOCK(conn, fsp, &lock)) { 267 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT); 268 return tevent_req_post(req, ev); 269 } 270 271 nread = read_file(fsp, 272 (char *)state->out_data.data, 273 in_offset, 274 in_length); 275 276 SMB_VFS_STRICT_UNLOCK(conn, fsp, &lock); 277 278 if (nread < 0) { 279 DEBUG(5,("smbd_smb2_read: read_file[%s] nread[%lld]\n", 280 fsp_str_dbg(fsp), (long long)nread)); 281 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); 282 return tevent_req_post(req, ev); 283 } 284 if (nread == 0 && in_length != 0) { 285 DEBUG(5,("smbd_smb2_read: read_file[%s] end of file\n", 286 fsp_str_dbg(fsp))); 287 tevent_req_nterror(req, NT_STATUS_END_OF_FILE); 288 return tevent_req_post(req, ev); 289 } 290 291 state->out_data.length = nread; 292 state->out_remaining = 0; 293 tevent_req_done(req); 294 return tevent_req_post(req, ev); 295} 296 297static void smbd_smb2_read_pipe_done(struct tevent_req *subreq) 298{ 299 struct tevent_req *req = tevent_req_callback_data(subreq, 300 struct tevent_req); 301 struct smbd_smb2_read_state *state = tevent_req_data(req, 302 struct smbd_smb2_read_state); 303 NTSTATUS status; 304 ssize_t nread = -1; 305 bool is_data_outstanding; 306 307 status = np_read_recv(subreq, &nread, &is_data_outstanding); 308 TALLOC_FREE(subreq); 309 if (!NT_STATUS_IS_OK(status)) { 310 tevent_req_nterror(req, status); 311 return; 312 } 313 314 if (nread == 0 && state->out_data.length != 0) { 315 tevent_req_nterror(req, NT_STATUS_END_OF_FILE); 316 return; 317 } 318 319 state->out_data.length = nread; 320 state->out_remaining = 0; 321 322 tevent_req_done(req); 323} 324 325static NTSTATUS smbd_smb2_read_recv(struct tevent_req *req, 326 TALLOC_CTX *mem_ctx, 327 DATA_BLOB *out_data, 328 uint32_t *out_remaining) 329{ 330 NTSTATUS status; 331 struct smbd_smb2_read_state *state = tevent_req_data(req, 332 struct smbd_smb2_read_state); 333 334 if (tevent_req_is_nterror(req, &status)) { 335 tevent_req_received(req); 336 return status; 337 } 338 339 *out_data = state->out_data; 340 talloc_steal(mem_ctx, out_data->data); 341 *out_remaining = state->out_remaining; 342 343 tevent_req_received(req); 344 return NT_STATUS_OK; 345} 346