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 "include/ntioctl.h" 27 #include "smb2_ioctl_private.h" 28 #include "librpc/gen_ndr/ioctl.h" smbd_smb2_fake_smb_request(struct smbd_smb2_request * req)29 30 #undef DBGC_CLASS 31 #define DBGC_CLASS DBGC_SMB2 32 33 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx, 34 struct tevent_context *ev, 35 struct smbd_smb2_request *smb2req, 36 struct files_struct *in_fsp, 37 uint32_t in_ctl_code, 38 DATA_BLOB in_input, 39 uint32_t in_max_output, 40 uint32_t in_flags); 41 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req, 42 TALLOC_CTX *mem_ctx, 43 DATA_BLOB *out_output, 44 bool *disconnect); 45 46 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq); 47 NTSTATUS smbd_smb2_request_process_ioctl(struct smbd_smb2_request *req) 48 { 49 NTSTATUS status; 50 const uint8_t *inbody; 51 uint32_t min_buffer_offset; 52 uint32_t max_buffer_offset; 53 uint32_t min_output_offset; 54 uint32_t allowed_length_in; 55 uint32_t allowed_length_out; 56 uint32_t in_ctl_code; 57 uint64_t in_file_id_persistent; 58 uint64_t in_file_id_volatile; 59 struct files_struct *in_fsp = NULL; 60 uint32_t in_input_offset; 61 uint32_t in_input_length; 62 DATA_BLOB in_input_buffer = data_blob_null; 63 uint32_t in_max_input_length; 64 uint32_t in_output_offset; 65 uint32_t in_output_length; 66 DATA_BLOB in_output_buffer = data_blob_null; 67 uint32_t in_max_output_length; 68 uint32_t in_flags; 69 uint32_t data_length_in; 70 uint32_t data_length_out; smbd_smb2_unread_bytes(struct smbd_smb2_request * req)71 uint32_t data_length_tmp; 72 uint32_t data_length_max; 73 struct tevent_req *subreq; 74 75 status = smbd_smb2_request_verify_sizes(req, 0x39); 76 if (!NT_STATUS_IS_OK(status)) { 77 return smbd_smb2_request_error(req, status); 78 } 79 inbody = SMBD_SMB2_IN_BODY_PTR(req); 80 81 in_ctl_code = IVAL(inbody, 0x04); 82 in_file_id_persistent = BVAL(inbody, 0x08); remove_smb2_chained_fsp(files_struct * fsp)83 in_file_id_volatile = BVAL(inbody, 0x10); 84 in_input_offset = IVAL(inbody, 0x18); 85 in_input_length = IVAL(inbody, 0x1C); 86 in_max_input_length = IVAL(inbody, 0x20); 87 in_output_offset = IVAL(inbody, 0x24); 88 in_output_length = IVAL(inbody, 0x28); 89 in_max_output_length = IVAL(inbody, 0x2C); 90 in_flags = IVAL(inbody, 0x30); 91 92 min_buffer_offset = SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req); 93 max_buffer_offset = min_buffer_offset + SMBD_SMB2_IN_DYN_LEN(req); 94 min_output_offset = min_buffer_offset; 95 96 /* 97 * InputOffset (4 bytes): The offset, in bytes, from the beginning of 98 * the SMB2 header to the input data buffer. If no input data is 99 * required for the FSCTL/IOCTL command being issued, the client SHOULD 100 * set this value to 0.<49> 101 * <49> If no input data is required for the FSCTL/IOCTL command being 102 * issued, Windows-based clients set this field to any value. 103 */ 104 allowed_length_in = 0; 105 if ((in_input_offset > 0) && (in_input_length > 0)) { 106 uint32_t tmp_ofs; 107 108 if (in_input_offset < min_buffer_offset) { 109 return smbd_smb2_request_error(req, 110 NT_STATUS_INVALID_PARAMETER); 111 } 112 if (in_input_offset > max_buffer_offset) { 113 return smbd_smb2_request_error(req, 114 NT_STATUS_INVALID_PARAMETER); 115 } 116 allowed_length_in = max_buffer_offset - in_input_offset; 117 118 tmp_ofs = in_input_offset - min_buffer_offset; 119 in_input_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); 120 in_input_buffer.data += tmp_ofs; 121 in_input_buffer.length = in_input_length; 122 min_output_offset += tmp_ofs; 123 min_output_offset += in_input_length; 124 } 125 126 if (in_input_length > allowed_length_in) { 127 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 128 } 129 130 allowed_length_out = 0; 131 if (in_output_offset > 0) { 132 if (in_output_offset < min_buffer_offset) { 133 return smbd_smb2_request_error(req, 134 NT_STATUS_INVALID_PARAMETER); 135 } 136 if (in_output_offset > max_buffer_offset) { 137 return smbd_smb2_request_error(req, 138 NT_STATUS_INVALID_PARAMETER); 139 } 140 allowed_length_out = max_buffer_offset - in_output_offset; 141 } 142 143 if (in_output_length > allowed_length_out) { 144 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 145 } 146 147 if (in_output_length > 0) { 148 uint32_t tmp_ofs; 149 150 if (in_output_offset < min_output_offset) { 151 return smbd_smb2_request_error(req, 152 NT_STATUS_INVALID_PARAMETER); 153 } 154 155 tmp_ofs = in_output_offset - min_buffer_offset; 156 in_output_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); 157 in_output_buffer.data += tmp_ofs; 158 in_output_buffer.length = in_output_length; 159 } 160 161 /* 162 * verify the credits and avoid overflows 163 * in_input_buffer.length and in_output_buffer.length 164 * are already verified. 165 */ 166 data_length_in = in_input_buffer.length + in_output_buffer.length; 167 168 data_length_out = in_max_input_length; 169 data_length_tmp = UINT32_MAX - data_length_out; 170 if (data_length_tmp < in_max_output_length) { 171 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 172 } 173 data_length_out += in_max_output_length; 174 175 data_length_max = MAX(data_length_in, data_length_out); 176 177 status = smbd_smb2_request_verify_creditcharge(req, data_length_max); 178 if (!NT_STATUS_IS_OK(status)) { 179 return smbd_smb2_request_error(req, status); 180 } 181 182 /* 183 * If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL the 184 * server MUST fail the request with STATUS_NOT_SUPPORTED. 185 */ 186 if (in_flags != SMB2_IOCTL_FLAG_IS_FSCTL) { 187 return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); 188 } 189 190 switch (in_ctl_code) { 191 case FSCTL_DFS_GET_REFERRALS: 192 case FSCTL_DFS_GET_REFERRALS_EX: 193 case FSCTL_PIPE_WAIT: 194 case FSCTL_VALIDATE_NEGOTIATE_INFO_224: 195 case FSCTL_VALIDATE_NEGOTIATE_INFO: 196 case FSCTL_QUERY_NETWORK_INTERFACE_INFO: 197 /* 198 * Some SMB2 specific CtlCodes like FSCTL_DFS_GET_REFERRALS or 199 * FSCTL_PIPE_WAIT does not take a file handle. 200 * 201 * If FileId in the SMB2 Header of the request is not 202 * 0xFFFFFFFFFFFFFFFF, then the server MUST fail the request 203 * with STATUS_INVALID_PARAMETER. 204 */ 205 if (in_file_id_persistent != UINT64_MAX || 206 in_file_id_volatile != UINT64_MAX) { 207 return smbd_smb2_request_error(req, 208 NT_STATUS_INVALID_PARAMETER); 209 } 210 break; 211 default: 212 in_fsp = file_fsp_smb2(req, in_file_id_persistent, 213 in_file_id_volatile); 214 if (in_fsp == NULL) { 215 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 216 } 217 break; 218 } 219 220 subreq = smbd_smb2_ioctl_send(req, req->sconn->ev_ctx, 221 req, in_fsp, 222 in_ctl_code, 223 in_input_buffer, 224 in_max_output_length, 225 in_flags); 226 if (subreq == NULL) { 227 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 228 } 229 tevent_req_set_callback(subreq, smbd_smb2_request_ioctl_done, req); 230 231 return smbd_smb2_request_pending_queue(req, subreq, 1000); 232 } 233 234 /* 235 * 3.3.4.4 Sending an Error Response 236 * An error code other than one of the following indicates a failure: 237 */ 238 static bool smbd_smb2_ioctl_is_failure(uint32_t ctl_code, NTSTATUS status, 239 size_t data_size) 240 { 241 if (NT_STATUS_IS_OK(status)) { 242 return false; 243 } 244 245 /* 246 * STATUS_BUFFER_OVERFLOW in a FSCTL_PIPE_TRANSCEIVE, FSCTL_PIPE_PEEK or 247 * FSCTL_DFS_GET_REFERRALS Response specified in section 2.2.32.<153> 248 */ 249 if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW) 250 && ((ctl_code == FSCTL_PIPE_TRANSCEIVE) 251 || (ctl_code == FSCTL_PIPE_PEEK) 252 || (ctl_code == FSCTL_DFS_GET_REFERRALS))) { 253 return false; 254 } 255 256 /* 257 * Any status other than STATUS_SUCCESS in a FSCTL_SRV_COPYCHUNK or 258 * FSCTL_SRV_COPYCHUNK_WRITE Response, when returning an 259 * SRV_COPYCHUNK_RESPONSE as described in section 2.2.32.1. 260 */ 261 if (((ctl_code == FSCTL_SRV_COPYCHUNK) 262 || (ctl_code == FSCTL_SRV_COPYCHUNK_WRITE)) 263 && (data_size == sizeof(struct srv_copychunk_rsp))) { 264 return false; 265 } 266 267 return true; 268 } 269 270 static void smbd_smb2_request_ioctl_done(struct tevent_req *subreq) 271 { 272 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 273 struct smbd_smb2_request); 274 const uint8_t *inbody; 275 DATA_BLOB outbody; 276 DATA_BLOB outdyn; 277 uint32_t in_ctl_code; 278 uint64_t in_file_id_persistent; 279 uint64_t in_file_id_volatile; 280 uint32_t out_input_offset; 281 uint32_t out_output_offset; 282 DATA_BLOB out_output_buffer = data_blob_null; 283 NTSTATUS status; 284 NTSTATUS error; /* transport error */ 285 bool disconnect = false; 286 287 status = smbd_smb2_ioctl_recv(subreq, req, 288 &out_output_buffer, 289 &disconnect); 290 291 DEBUG(10,("smbd_smb2_request_ioctl_done: smbd_smb2_ioctl_recv returned " 292 "%u status %s\n", 293 (unsigned int)out_output_buffer.length, 294 nt_errstr(status) )); 295 296 TALLOC_FREE(subreq); 297 if (disconnect) { 298 error = status; 299 smbd_server_connection_terminate(req->xconn, 300 nt_errstr(error)); 301 return; 302 } 303 304 inbody = SMBD_SMB2_IN_BODY_PTR(req); 305 306 in_ctl_code = IVAL(inbody, 0x04); 307 in_file_id_persistent = BVAL(inbody, 0x08); 308 in_file_id_volatile = BVAL(inbody, 0x10); 309 310 if (smbd_smb2_ioctl_is_failure(in_ctl_code, status, 311 out_output_buffer.length)) { 312 error = smbd_smb2_request_error(req, status); 313 if (!NT_STATUS_IS_OK(error)) { 314 smbd_server_connection_terminate(req->xconn, 315 nt_errstr(error)); 316 return; 317 } 318 return; 319 } 320 321 out_input_offset = SMB2_HDR_BODY + 0x30; 322 out_output_offset = SMB2_HDR_BODY + 0x30; 323 324 outbody = smbd_smb2_generate_outbody(req, 0x30); 325 if (outbody.data == NULL) { 326 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 327 if (!NT_STATUS_IS_OK(error)) { 328 smbd_server_connection_terminate(req->xconn, 329 nt_errstr(error)); 330 return; 331 } 332 return; 333 } 334 335 SSVAL(outbody.data, 0x00, 0x30 + 1); /* struct size */ 336 SSVAL(outbody.data, 0x02, 0); /* reserved */ 337 SIVAL(outbody.data, 0x04, 338 in_ctl_code); /* ctl code */ 339 SBVAL(outbody.data, 0x08, 340 in_file_id_persistent); /* file id (persistent) */ 341 SBVAL(outbody.data, 0x10, 342 in_file_id_volatile); /* file id (volatile) */ 343 SIVAL(outbody.data, 0x18, 344 out_input_offset); /* input offset */ 345 SIVAL(outbody.data, 0x1C, 0); /* input count */ 346 SIVAL(outbody.data, 0x20, 347 out_output_offset); /* output offset */ 348 SIVAL(outbody.data, 0x24, 349 out_output_buffer.length); /* output count */ 350 SIVAL(outbody.data, 0x28, 0); /* flags */ 351 SIVAL(outbody.data, 0x2C, 0); /* reserved */ 352 353 /* 354 * Note: Windows Vista and 2008 send back also the 355 * input from the request. But it was fixed in 356 * Windows 7. 357 */ 358 outdyn = out_output_buffer; 359 360 error = smbd_smb2_request_done_ex(req, status, outbody, &outdyn, 361 __location__); 362 if (!NT_STATUS_IS_OK(error)) { 363 smbd_server_connection_terminate(req->xconn, 364 nt_errstr(error)); 365 return; 366 } 367 } 368 369 static struct tevent_req *smbd_smb2_ioctl_send(TALLOC_CTX *mem_ctx, 370 struct tevent_context *ev, 371 struct smbd_smb2_request *smb2req, 372 struct files_struct *fsp, 373 uint32_t in_ctl_code, 374 DATA_BLOB in_input, 375 uint32_t in_max_output, 376 uint32_t in_flags) 377 { 378 struct tevent_req *req; 379 struct smbd_smb2_ioctl_state *state; 380 struct smb_request *smbreq; 381 382 req = tevent_req_create(mem_ctx, &state, 383 struct smbd_smb2_ioctl_state); 384 if (req == NULL) { 385 return NULL; 386 } 387 state->smb2req = smb2req; 388 state->smbreq = NULL; 389 state->fsp = fsp; 390 state->in_input = in_input; 391 state->in_max_output = in_max_output; 392 state->out_output = data_blob_null; 393 394 DEBUG(10, ("smbd_smb2_ioctl: ctl_code[0x%08x] %s, %s\n", 395 (unsigned)in_ctl_code, 396 fsp ? fsp_str_dbg(fsp) : "<no handle>", 397 fsp_fnum_dbg(fsp))); 398 399 smbreq = smbd_smb2_fake_smb_request(smb2req); 400 if (tevent_req_nomem(smbreq, req)) { 401 return tevent_req_post(req, ev); 402 } 403 state->smbreq = smbreq; 404 405 switch (in_ctl_code & IOCTL_DEV_TYPE_MASK) { 406 case FSCTL_DFS: 407 return smb2_ioctl_dfs(in_ctl_code, ev, req, state); 408 break; 409 case FSCTL_FILESYSTEM: 410 return smb2_ioctl_filesys(in_ctl_code, ev, req, state); 411 break; 412 case FSCTL_NAMED_PIPE: 413 return smb2_ioctl_named_pipe(in_ctl_code, ev, req, state); 414 break; 415 case FSCTL_NETWORK_FILESYSTEM: 416 return smb2_ioctl_network_fs(in_ctl_code, ev, req, state); 417 break; 418 default: 419 if (IS_IPC(smbreq->conn)) { 420 tevent_req_nterror(req, NT_STATUS_FS_DRIVER_REQUIRED); 421 } else { 422 tevent_req_nterror(req, NT_STATUS_INVALID_DEVICE_REQUEST); 423 } 424 425 return tevent_req_post(req, ev); 426 break; 427 } 428 429 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); 430 return tevent_req_post(req, ev); 431 } 432 433 static NTSTATUS smbd_smb2_ioctl_recv(struct tevent_req *req, 434 TALLOC_CTX *mem_ctx, 435 DATA_BLOB *out_output, 436 bool *disconnect) 437 { 438 NTSTATUS status = NT_STATUS_OK; 439 struct smbd_smb2_ioctl_state *state = tevent_req_data(req, 440 struct smbd_smb2_ioctl_state); 441 enum tevent_req_state req_state; 442 uint64_t err; 443 444 *disconnect = state->disconnect; 445 446 if ((tevent_req_is_error(req, &req_state, &err) == false) 447 || (req_state == TEVENT_REQ_USER_ERROR)) { 448 /* 449 * Return output buffer to caller if the ioctl was successfully 450 * processed, even if a user error occurred. Some ioctls return 451 * data on failure. 452 */ 453 *out_output = state->out_output; 454 talloc_steal(mem_ctx, out_output->data); 455 } 456 457 tevent_req_is_nterror(req, &status); 458 tevent_req_received(req); 459 return status; 460 } 461