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_find_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint8_t in_file_info_class, 29 uint8_t in_flags, 30 uint32_t in_file_index, 31 uint64_t in_file_id_volatile, 32 uint32_t in_output_buffer_length, 33 const char *in_file_name); 34static NTSTATUS smbd_smb2_find_recv(struct tevent_req *req, 35 TALLOC_CTX *mem_ctx, 36 DATA_BLOB *out_output_buffer); 37 38static void smbd_smb2_request_find_done(struct tevent_req *subreq); 39NTSTATUS smbd_smb2_request_process_find(struct smbd_smb2_request *req) 40{ 41 const uint8_t *inhdr; 42 const uint8_t *inbody; 43 int i = req->current_idx; 44 size_t expected_body_size = 0x21; 45 size_t body_size; 46 uint8_t in_file_info_class; 47 uint8_t in_flags; 48 uint32_t in_file_index; 49 uint64_t in_file_id_persistent; 50 uint64_t in_file_id_volatile; 51 uint16_t in_file_name_offset; 52 uint16_t in_file_name_length; 53 DATA_BLOB in_file_name_buffer; 54 char *in_file_name_string; 55 size_t in_file_name_string_size; 56 uint32_t in_output_buffer_length; 57 struct tevent_req *subreq; 58 bool ok; 59 60 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 61 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 62 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 63 } 64 65 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 66 67 body_size = SVAL(inbody, 0x00); 68 if (body_size != expected_body_size) { 69 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 70 } 71 72 in_file_info_class = CVAL(inbody, 0x02); 73 in_flags = CVAL(inbody, 0x03); 74 in_file_index = IVAL(inbody, 0x04); 75 in_file_id_persistent = BVAL(inbody, 0x08); 76 in_file_id_volatile = BVAL(inbody, 0x10); 77 in_file_name_offset = SVAL(inbody, 0x18); 78 in_file_name_length = SVAL(inbody, 0x1A); 79 in_output_buffer_length = IVAL(inbody, 0x1C); 80 81 if (in_file_name_offset == 0 && in_file_name_length == 0) { 82 /* This is ok */ 83 } else if (in_file_name_offset != 84 (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) { 85 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 86 } 87 88 if (in_file_name_length > req->in.vector[i+2].iov_len) { 89 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 90 } 91 92 in_file_name_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base; 93 in_file_name_buffer.length = in_file_name_length; 94 95 ok = convert_string_talloc(req, CH_UTF16, CH_UNIX, 96 in_file_name_buffer.data, 97 in_file_name_buffer.length, 98 &in_file_name_string, 99 &in_file_name_string_size, false); 100 if (!ok) { 101 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER); 102 } 103 104 if (req->compat_chain_fsp) { 105 /* skip check */ 106 } else if (in_file_id_persistent != 0) { 107 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 108 } 109 110 subreq = smbd_smb2_find_send(req, 111 req->sconn->smb2.event_ctx, 112 req, 113 in_file_info_class, 114 in_flags, 115 in_file_index, 116 in_file_id_volatile, 117 in_output_buffer_length, 118 in_file_name_string); 119 if (subreq == NULL) { 120 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 121 } 122 tevent_req_set_callback(subreq, smbd_smb2_request_find_done, req); 123 124 return smbd_smb2_request_pending_queue(req, subreq); 125} 126 127static void smbd_smb2_request_find_done(struct tevent_req *subreq) 128{ 129 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 130 struct smbd_smb2_request); 131 int i = req->current_idx; 132 uint8_t *outhdr; 133 DATA_BLOB outbody; 134 DATA_BLOB outdyn; 135 uint16_t out_output_buffer_offset; 136 DATA_BLOB out_output_buffer = data_blob_null; 137 NTSTATUS status; 138 NTSTATUS error; /* transport error */ 139 140 status = smbd_smb2_find_recv(subreq, 141 req, 142 &out_output_buffer); 143 TALLOC_FREE(subreq); 144 if (!NT_STATUS_IS_OK(status)) { 145 error = smbd_smb2_request_error(req, status); 146 if (!NT_STATUS_IS_OK(error)) { 147 smbd_server_connection_terminate(req->sconn, 148 nt_errstr(error)); 149 return; 150 } 151 return; 152 } 153 154 out_output_buffer_offset = SMB2_HDR_BODY + 0x08; 155 156 outhdr = (uint8_t *)req->out.vector[i].iov_base; 157 158 outbody = data_blob_talloc(req->out.vector, NULL, 0x08); 159 if (outbody.data == NULL) { 160 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 161 if (!NT_STATUS_IS_OK(error)) { 162 smbd_server_connection_terminate(req->sconn, 163 nt_errstr(error)); 164 return; 165 } 166 return; 167 } 168 169 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */ 170 SSVAL(outbody.data, 0x02, 171 out_output_buffer_offset); /* output buffer offset */ 172 SIVAL(outbody.data, 0x04, 173 out_output_buffer.length); /* output buffer length */ 174 175 outdyn = out_output_buffer; 176 177 error = smbd_smb2_request_done(req, outbody, &outdyn); 178 if (!NT_STATUS_IS_OK(error)) { 179 smbd_server_connection_terminate(req->sconn, 180 nt_errstr(error)); 181 return; 182 } 183} 184 185struct smbd_smb2_find_state { 186 struct smbd_smb2_request *smb2req; 187 DATA_BLOB out_output_buffer; 188}; 189 190static struct tevent_req *smbd_smb2_find_send(TALLOC_CTX *mem_ctx, 191 struct tevent_context *ev, 192 struct smbd_smb2_request *smb2req, 193 uint8_t in_file_info_class, 194 uint8_t in_flags, 195 uint32_t in_file_index, 196 uint64_t in_file_id_volatile, 197 uint32_t in_output_buffer_length, 198 const char *in_file_name) 199{ 200 struct tevent_req *req; 201 struct smbd_smb2_find_state *state; 202 struct smb_request *smbreq; 203 connection_struct *conn = smb2req->tcon->compat_conn; 204 files_struct *fsp; 205 NTSTATUS status; 206 NTSTATUS empty_status; 207 uint32_t info_level; 208 uint32_t max_count; 209 char *pdata; 210 char *base_data; 211 char *end_data; 212 int last_entry_off = 0; 213 uint64_t off = 0; 214 uint32_t num = 0; 215 uint32_t dirtype = aHIDDEN | aSYSTEM | aDIR; 216 const char *directory; 217 bool dont_descend = false; 218 bool ask_sharemode = true; 219 220 req = tevent_req_create(mem_ctx, &state, 221 struct smbd_smb2_find_state); 222 if (req == NULL) { 223 return NULL; 224 } 225 state->smb2req = smb2req; 226 state->out_output_buffer = data_blob_null; 227 228 DEBUG(10,("smbd_smb2_find_send: file_id[0x%016llX]\n", 229 (unsigned long long)in_file_id_volatile)); 230 231 smbreq = smbd_smb2_fake_smb_request(smb2req); 232 if (tevent_req_nomem(smbreq, req)) { 233 return tevent_req_post(req, ev); 234 } 235 236 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 237 if (fsp == NULL) { 238 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 239 return tevent_req_post(req, ev); 240 } 241 if (conn != fsp->conn) { 242 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 243 return tevent_req_post(req, ev); 244 } 245 if (smb2req->session->vuid != fsp->vuid) { 246 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 247 return tevent_req_post(req, ev); 248 } 249 250 if (!fsp->is_directory) { 251 tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); 252 return tevent_req_post(req, ev); 253 } 254 255 directory = fsp->fsp_name->base_name; 256 257 if (strcmp(in_file_name, "") == 0) { 258 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); 259 return tevent_req_post(req, ev); 260 } 261 if (strcmp(in_file_name, "\\") == 0) { 262 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); 263 return tevent_req_post(req, ev); 264 } 265 if (strcmp(in_file_name, "/") == 0) { 266 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_INVALID); 267 return tevent_req_post(req, ev); 268 } 269 270 if (in_output_buffer_length > 0x10000) { 271 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); 272 return tevent_req_post(req, ev); 273 } 274 275 switch (in_file_info_class) { 276 case SMB2_FIND_DIRECTORY_INFO: 277 info_level = SMB_FIND_FILE_DIRECTORY_INFO; 278 break; 279 280 case SMB2_FIND_FULL_DIRECTORY_INFO: 281 info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO; 282 break; 283 284 case SMB2_FIND_BOTH_DIRECTORY_INFO: 285 info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO; 286 break; 287 288 case SMB2_FIND_NAME_INFO: 289 info_level = SMB_FIND_FILE_NAMES_INFO; 290 break; 291 292 case SMB2_FIND_ID_BOTH_DIRECTORY_INFO: 293 info_level = SMB_FIND_ID_BOTH_DIRECTORY_INFO; 294 break; 295 296 case SMB2_FIND_ID_FULL_DIRECTORY_INFO: 297 info_level = SMB_FIND_ID_FULL_DIRECTORY_INFO; 298 break; 299 300 default: 301 tevent_req_nterror(req, NT_STATUS_INVALID_INFO_CLASS); 302 return tevent_req_post(req, ev); 303 } 304 305 if (in_flags & SMB2_CONTINUE_FLAG_REOPEN) { 306 if (fsp->dptr) { 307 dptr_CloseDir(fsp->dptr); 308 fsp->dptr = NULL; 309 } 310 } 311 312 if (fsp->dptr == NULL) { 313 bool wcard_has_wild; 314 315 if (!(fsp->access_mask & SEC_DIR_LIST)) { 316 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); 317 return tevent_req_post(req, ev); 318 } 319 320 wcard_has_wild = ms_has_wild(in_file_name); 321 322 status = dptr_create(conn, 323 directory, 324 false, /* old_handle */ 325 false, /* expect_close */ 326 0, /* spid */ 327 in_file_name, /* wcard */ 328 wcard_has_wild, 329 dirtype, 330 &fsp->dptr); 331 if (!NT_STATUS_IS_OK(status)) { 332 tevent_req_nterror(req, status); 333 return tevent_req_post(req, ev); 334 } 335 336 empty_status = NT_STATUS_NO_SUCH_FILE; 337 } else { 338 empty_status = STATUS_NO_MORE_FILES; 339 } 340 341 if (in_flags & SMB2_CONTINUE_FLAG_RESTART) { 342 dptr_SeekDir(fsp->dptr, 0); 343 } 344 345 if (in_flags & SMB2_CONTINUE_FLAG_SINGLE) { 346 max_count = 1; 347 } else { 348 max_count = UINT16_MAX; 349 } 350 351#define DIR_ENTRY_SAFETY_MARGIN 4096 352 353 state->out_output_buffer = data_blob_talloc(state, NULL, 354 in_output_buffer_length + DIR_ENTRY_SAFETY_MARGIN); 355 if (tevent_req_nomem(state->out_output_buffer.data, req)) { 356 return tevent_req_post(req, ev); 357 } 358 359 state->out_output_buffer.length = 0; 360 pdata = (char *)state->out_output_buffer.data; 361 base_data = pdata; 362 end_data = pdata + in_output_buffer_length; 363 last_entry_off = 0; 364 off = 0; 365 num = 0; 366 367 DEBUG(8,("smbd_smb2_find_send: dirpath=<%s> dontdescend=<%s>\n", 368 directory, lp_dontdescend(SNUM(conn)))); 369 if (in_list(directory,lp_dontdescend(SNUM(conn)),conn->case_sensitive)) { 370 dont_descend = true; 371 } 372 373 ask_sharemode = lp_parm_bool(SNUM(conn), 374 "smbd", "search ask sharemode", 375 true); 376 377 while (true) { 378 bool ok; 379 bool got_exact_match = false; 380 bool out_of_space = false; 381 int space_remaining = in_output_buffer_length - off; 382 383 ok = smbd_dirptr_lanman2_entry(state, 384 conn, 385 fsp->dptr, 386 smbreq->flags2, 387 in_file_name, 388 dirtype, 389 info_level, 390 false, /* requires_resume_key */ 391 dont_descend, 392 ask_sharemode, 393 8, /* align to 8 bytes */ 394 false, /* no padding */ 395 &pdata, 396 base_data, 397 end_data, 398 space_remaining, 399 &out_of_space, 400 &got_exact_match, 401 &last_entry_off, 402 NULL); 403 404 off = PTR_DIFF(pdata, base_data); 405 406 if (!ok) { 407 if (num > 0) { 408 SIVAL(state->out_output_buffer.data, last_entry_off, 0); 409 tevent_req_done(req); 410 return tevent_req_post(req, ev); 411 } else if (out_of_space) { 412 tevent_req_nterror(req, NT_STATUS_INFO_LENGTH_MISMATCH); 413 return tevent_req_post(req, ev); 414 } else { 415 tevent_req_nterror(req, empty_status); 416 return tevent_req_post(req, ev); 417 } 418 } 419 420 num++; 421 state->out_output_buffer.length = off; 422 423 if (num < max_count) { 424 continue; 425 } 426 427 SIVAL(state->out_output_buffer.data, last_entry_off, 0); 428 tevent_req_done(req); 429 return tevent_req_post(req, ev); 430 } 431 432 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); 433 return tevent_req_post(req, ev); 434} 435 436static NTSTATUS smbd_smb2_find_recv(struct tevent_req *req, 437 TALLOC_CTX *mem_ctx, 438 DATA_BLOB *out_output_buffer) 439{ 440 NTSTATUS status; 441 struct smbd_smb2_find_state *state = tevent_req_data(req, 442 struct smbd_smb2_find_state); 443 444 if (tevent_req_is_nterror(req, &status)) { 445 tevent_req_received(req); 446 return status; 447 } 448 449 *out_output_buffer = state->out_output_buffer; 450 talloc_steal(mem_ctx, out_output_buffer->data); 451 452 tevent_req_received(req); 453 return NT_STATUS_OK; 454} 455