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_notify_send(TALLOC_CTX *mem_ctx, 26 struct tevent_context *ev, 27 struct smbd_smb2_request *smb2req, 28 uint16_t in_flags, 29 uint32_t in_output_buffer_length, 30 uint64_t in_file_id_volatile, 31 uint64_t in_completion_filter); 32static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req, 33 TALLOC_CTX *mem_ctx, 34 DATA_BLOB *out_output_buffer); 35 36static void smbd_smb2_request_notify_done(struct tevent_req *subreq); 37NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req) 38{ 39 const uint8_t *inhdr; 40 const uint8_t *inbody; 41 int i = req->current_idx; 42 size_t expected_body_size = 0x20; 43 size_t body_size; 44 uint16_t in_flags; 45 uint32_t in_output_buffer_length; 46 uint64_t in_file_id_persistent; 47 uint64_t in_file_id_volatile; 48 uint64_t in_completion_filter; 49 struct tevent_req *subreq; 50 51 inhdr = (const uint8_t *)req->in.vector[i+0].iov_base; 52 if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) { 53 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 54 } 55 56 inbody = (const uint8_t *)req->in.vector[i+1].iov_base; 57 58 body_size = SVAL(inbody, 0x00); 59 if (body_size != expected_body_size) { 60 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 61 } 62 63 in_flags = SVAL(inbody, 0x02); 64 in_output_buffer_length = IVAL(inbody, 0x04); 65 in_file_id_persistent = BVAL(inbody, 0x08); 66 in_file_id_volatile = BVAL(inbody, 0x10); 67 in_completion_filter = IVAL(inbody, 0x18); 68 69 /* 70 * 0x00010000 is what Windows 7 uses, 71 * Windows 2008 uses 0x00080000 72 */ 73 if (in_output_buffer_length > 0x00010000) { 74 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); 75 } 76 77 if (req->compat_chain_fsp) { 78 /* skip check */ 79 } else if (in_file_id_persistent != 0) { 80 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED); 81 } 82 83 subreq = smbd_smb2_notify_send(req, 84 req->sconn->smb2.event_ctx, 85 req, 86 in_flags, 87 in_output_buffer_length, 88 in_file_id_volatile, 89 in_completion_filter); 90 if (subreq == NULL) { 91 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 92 } 93 tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req); 94 95 return smbd_smb2_request_pending_queue(req, subreq); 96} 97 98static void smbd_smb2_request_notify_done(struct tevent_req *subreq) 99{ 100 struct smbd_smb2_request *req = tevent_req_callback_data(subreq, 101 struct smbd_smb2_request); 102 int i = req->current_idx; 103 uint8_t *outhdr; 104 DATA_BLOB outbody; 105 DATA_BLOB outdyn; 106 uint16_t out_output_buffer_offset; 107 DATA_BLOB out_output_buffer = data_blob_null; 108 NTSTATUS status; 109 NTSTATUS error; /* transport error */ 110 111 status = smbd_smb2_notify_recv(subreq, 112 req, 113 &out_output_buffer); 114 TALLOC_FREE(subreq); 115 if (!NT_STATUS_IS_OK(status)) { 116 error = smbd_smb2_request_error(req, status); 117 if (!NT_STATUS_IS_OK(error)) { 118 smbd_server_connection_terminate(req->sconn, 119 nt_errstr(error)); 120 return; 121 } 122 return; 123 } 124 125 out_output_buffer_offset = SMB2_HDR_BODY + 0x08; 126 127 outhdr = (uint8_t *)req->out.vector[i].iov_base; 128 129 outbody = data_blob_talloc(req->out.vector, NULL, 0x08); 130 if (outbody.data == NULL) { 131 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); 132 if (!NT_STATUS_IS_OK(error)) { 133 smbd_server_connection_terminate(req->sconn, 134 nt_errstr(error)); 135 return; 136 } 137 return; 138 } 139 140 SSVAL(outbody.data, 0x00, 0x08 + 1); /* struct size */ 141 SSVAL(outbody.data, 0x02, 142 out_output_buffer_offset); /* output buffer offset */ 143 SIVAL(outbody.data, 0x04, 144 out_output_buffer.length); /* output buffer length */ 145 146 outdyn = out_output_buffer; 147 148 error = smbd_smb2_request_done(req, outbody, &outdyn); 149 if (!NT_STATUS_IS_OK(error)) { 150 smbd_server_connection_terminate(req->sconn, 151 nt_errstr(error)); 152 return; 153 } 154} 155 156struct smbd_smb2_notify_state { 157 struct smbd_smb2_request *smb2req; 158 struct smb_request *smbreq; 159 struct tevent_immediate *im; 160 NTSTATUS status; 161 DATA_BLOB out_output_buffer; 162}; 163 164static void smbd_smb2_notify_reply(struct smb_request *smbreq, 165 NTSTATUS error_code, 166 uint8_t *buf, size_t len); 167static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx, 168 struct tevent_immediate *im, 169 void *private_data); 170static bool smbd_smb2_notify_cancel(struct tevent_req *req); 171 172static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx, 173 struct tevent_context *ev, 174 struct smbd_smb2_request *smb2req, 175 uint16_t in_flags, 176 uint32_t in_output_buffer_length, 177 uint64_t in_file_id_volatile, 178 uint64_t in_completion_filter) 179{ 180 struct tevent_req *req; 181 struct smbd_smb2_notify_state *state; 182 struct smb_request *smbreq; 183 connection_struct *conn = smb2req->tcon->compat_conn; 184 files_struct *fsp; 185 bool recursive = (in_flags & 0x0001) ? true : false; 186 NTSTATUS status; 187 188 req = tevent_req_create(mem_ctx, &state, 189 struct smbd_smb2_notify_state); 190 if (req == NULL) { 191 return NULL; 192 } 193 state->smb2req = smb2req; 194 state->status = NT_STATUS_INTERNAL_ERROR; 195 state->out_output_buffer = data_blob_null; 196 state->im = NULL; 197 198 DEBUG(10,("smbd_smb2_notify_send: file_id[0x%016llX]\n", 199 (unsigned long long)in_file_id_volatile)); 200 201 smbreq = smbd_smb2_fake_smb_request(smb2req); 202 if (tevent_req_nomem(smbreq, req)) { 203 return tevent_req_post(req, ev); 204 } 205 206 state->smbreq = smbreq; 207 smbreq->async_priv = (void *)req; 208 209 fsp = file_fsp(smbreq, (uint16_t)in_file_id_volatile); 210 if (fsp == NULL) { 211 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 212 return tevent_req_post(req, ev); 213 } 214 if (conn != fsp->conn) { 215 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 216 return tevent_req_post(req, ev); 217 } 218 if (smb2req->session->vuid != fsp->vuid) { 219 tevent_req_nterror(req, NT_STATUS_FILE_CLOSED); 220 return tevent_req_post(req, ev); 221 } 222 223 { 224 char *filter_string; 225 226 filter_string = notify_filter_string(NULL, in_completion_filter); 227 if (tevent_req_nomem(filter_string, req)) { 228 return tevent_req_post(req, ev); 229 } 230 231 DEBUG(3,("smbd_smb2_notify_send: notify change " 232 "called on %s, filter = %s, recursive = %d\n", 233 fsp_str_dbg(fsp), filter_string, recursive)); 234 235 TALLOC_FREE(filter_string); 236 } 237 238 if ((!fsp->is_directory) || (conn != fsp->conn)) { 239 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); 240 return tevent_req_post(req, ev); 241 } 242 243 if (fsp->notify == NULL) { 244 245 status = change_notify_create(fsp, 246 in_completion_filter, 247 recursive); 248 if (!NT_STATUS_IS_OK(status)) { 249 DEBUG(10, ("change_notify_create returned %s\n", 250 nt_errstr(status))); 251 tevent_req_nterror(req, status); 252 return tevent_req_post(req, ev); 253 } 254 } 255 256 if (fsp->notify->num_changes != 0) { 257 258 /* 259 * We've got changes pending, respond immediately 260 */ 261 262 /* 263 * TODO: write a torture test to check the filtering behaviour 264 * here. 265 */ 266 267 change_notify_reply(fsp->conn, smbreq, 268 NT_STATUS_OK, 269 in_output_buffer_length, 270 fsp->notify, 271 smbd_smb2_notify_reply); 272 273 /* 274 * change_notify_reply() above has independently 275 * called tevent_req_done(). 276 */ 277 return tevent_req_post(req, ev); 278 } 279 280 state->im = tevent_create_immediate(state); 281 if (tevent_req_nomem(state->im, req)) { 282 return tevent_req_post(req, ev); 283 } 284 285 /* 286 * No changes pending, queue the request 287 */ 288 289 status = change_notify_add_request(smbreq, 290 in_output_buffer_length, 291 in_completion_filter, 292 recursive, fsp, 293 smbd_smb2_notify_reply); 294 if (!NT_STATUS_IS_OK(status)) { 295 tevent_req_nterror(req, status); 296 return tevent_req_post(req, ev); 297 } 298 299 /* allow this request to be canceled */ 300 tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel); 301 302 return req; 303} 304 305static void smbd_smb2_notify_reply(struct smb_request *smbreq, 306 NTSTATUS error_code, 307 uint8_t *buf, size_t len) 308{ 309 struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv, 310 struct tevent_req); 311 struct smbd_smb2_notify_state *state = tevent_req_data(req, 312 struct smbd_smb2_notify_state); 313 314 state->status = error_code; 315 if (!NT_STATUS_IS_OK(error_code)) { 316 /* nothing */ 317 } else if (len == 0) { 318 state->status = STATUS_NOTIFY_ENUM_DIR; 319 } else { 320 state->out_output_buffer = data_blob_talloc(state, buf, len); 321 if (state->out_output_buffer.data == NULL) { 322 state->status = NT_STATUS_NO_MEMORY; 323 } 324 } 325 326 if (state->im == NULL) { 327 smbd_smb2_notify_reply_trigger(NULL, NULL, req); 328 return; 329 } 330 331 /* 332 * if this is called async, we need to go via an immediate event 333 * because the caller replies on the smb_request (a child of req 334 * being arround after calling this function 335 */ 336 tevent_schedule_immediate(state->im, 337 state->smb2req->sconn->smb2.event_ctx, 338 smbd_smb2_notify_reply_trigger, 339 req); 340} 341 342static void smbd_smb2_notify_reply_trigger(struct tevent_context *ctx, 343 struct tevent_immediate *im, 344 void *private_data) 345{ 346 struct tevent_req *req = talloc_get_type_abort(private_data, 347 struct tevent_req); 348 struct smbd_smb2_notify_state *state = tevent_req_data(req, 349 struct smbd_smb2_notify_state); 350 351 if (!NT_STATUS_IS_OK(state->status)) { 352 tevent_req_nterror(req, state->status); 353 return; 354 } 355 356 tevent_req_done(req); 357} 358 359static bool smbd_smb2_notify_cancel(struct tevent_req *req) 360{ 361 struct smbd_smb2_notify_state *state = tevent_req_data(req, 362 struct smbd_smb2_notify_state); 363 364 smbd_notify_cancel_by_smbreq(state->smb2req->sconn, 365 state->smbreq); 366 367 return true; 368} 369 370static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req, 371 TALLOC_CTX *mem_ctx, 372 DATA_BLOB *out_output_buffer) 373{ 374 NTSTATUS status; 375 struct smbd_smb2_notify_state *state = tevent_req_data(req, 376 struct smbd_smb2_notify_state); 377 378 if (tevent_req_is_nterror(req, &status)) { 379 tevent_req_received(req); 380 return status; 381 } 382 383 *out_output_buffer = state->out_output_buffer; 384 talloc_steal(mem_ctx, out_output_buffer->data); 385 386 tevent_req_received(req); 387 return NT_STATUS_OK; 388} 389