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 "../libcli/security/security.h"
26 : #include "auth.h"
27 : #include "lib/param/loadparm.h"
28 : #include "../lib/util/tevent_ntstatus.h"
29 :
30 : #undef DBGC_CLASS
31 : #define DBGC_CLASS DBGC_SMB2
32 :
33 : static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx,
34 : struct tevent_context *ev,
35 : struct smbd_smb2_request *smb2req,
36 : uint16_t in_flags,
37 : const char *in_path);
38 : static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req,
39 : uint8_t *out_share_type,
40 : uint32_t *out_share_flags,
41 : uint32_t *out_capabilities,
42 : uint32_t *out_maximal_access,
43 : uint32_t *out_tree_id,
44 : bool *disconnect);
45 :
46 : static void smbd_smb2_request_tcon_done(struct tevent_req *subreq);
47 :
48 5609 : NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
49 : {
50 5609 : struct smbXsrv_connection *xconn = req->xconn;
51 : const uint8_t *inbody;
52 : uint16_t in_flags;
53 : uint16_t in_path_offset;
54 : uint16_t in_path_length;
55 : DATA_BLOB in_path_buffer;
56 : char *in_path_string;
57 : size_t in_path_string_size;
58 : NTSTATUS status;
59 : bool ok;
60 : struct tevent_req *subreq;
61 :
62 5609 : status = smbd_smb2_request_verify_sizes(req, 0x09);
63 5609 : if (!NT_STATUS_IS_OK(status)) {
64 0 : return smbd_smb2_request_error(req, status);
65 : }
66 5609 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
67 :
68 5609 : if (xconn->protocol >= PROTOCOL_SMB3_11) {
69 3253 : in_flags = SVAL(inbody, 0x02);
70 : } else {
71 2356 : in_flags = 0;
72 : }
73 5609 : in_path_offset = SVAL(inbody, 0x04);
74 5609 : in_path_length = SVAL(inbody, 0x06);
75 :
76 5609 : if (in_path_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) {
77 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
78 : }
79 :
80 5609 : if (in_path_length > SMBD_SMB2_IN_DYN_LEN(req)) {
81 0 : return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
82 : }
83 :
84 5609 : in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
85 5609 : in_path_buffer.length = in_path_length;
86 :
87 9409 : ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
88 5609 : in_path_buffer.data,
89 : in_path_buffer.length,
90 : &in_path_string,
91 : &in_path_string_size);
92 5609 : if (!ok) {
93 0 : return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
94 : }
95 :
96 5609 : if (in_path_buffer.length == 0) {
97 0 : in_path_string_size = 0;
98 : }
99 :
100 5609 : if (strlen(in_path_string) != in_path_string_size) {
101 0 : return smbd_smb2_request_error(req, NT_STATUS_BAD_NETWORK_NAME);
102 : }
103 :
104 9409 : subreq = smbd_smb2_tree_connect_send(req,
105 5609 : req->sconn->ev_ctx,
106 : req,
107 : in_flags,
108 : in_path_string);
109 5609 : if (subreq == NULL) {
110 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
111 : }
112 5609 : tevent_req_set_callback(subreq, smbd_smb2_request_tcon_done, req);
113 :
114 : /*
115 : * Avoid sending a STATUS_PENDING message, it's very likely
116 : * the client won't expect that.
117 : */
118 5609 : return smbd_smb2_request_pending_queue(req, subreq, 0);
119 : }
120 :
121 5609 : static void smbd_smb2_request_tcon_done(struct tevent_req *subreq)
122 : {
123 3800 : struct smbd_smb2_request *req =
124 5609 : tevent_req_callback_data(subreq,
125 : struct smbd_smb2_request);
126 : uint8_t *outhdr;
127 : DATA_BLOB outbody;
128 5609 : uint8_t out_share_type = 0;
129 5609 : uint32_t out_share_flags = 0;
130 5609 : uint32_t out_capabilities = 0;
131 5609 : uint32_t out_maximal_access = 0;
132 5609 : uint32_t out_tree_id = 0;
133 5609 : bool disconnect = false;
134 : NTSTATUS status;
135 : NTSTATUS error;
136 :
137 5609 : status = smbd_smb2_tree_connect_recv(subreq,
138 : &out_share_type,
139 : &out_share_flags,
140 : &out_capabilities,
141 : &out_maximal_access,
142 : &out_tree_id,
143 : &disconnect);
144 5609 : TALLOC_FREE(subreq);
145 5609 : if (!NT_STATUS_IS_OK(status)) {
146 14 : if (disconnect) {
147 0 : smbd_server_connection_terminate(req->xconn,
148 : nt_errstr(status));
149 7 : return;
150 : }
151 14 : error = smbd_smb2_request_error(req, status);
152 14 : if (!NT_STATUS_IS_OK(error)) {
153 0 : smbd_server_connection_terminate(req->xconn,
154 : nt_errstr(error));
155 0 : return;
156 : }
157 14 : return;
158 : }
159 :
160 5595 : outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
161 :
162 5595 : outbody = smbd_smb2_generate_outbody(req, 0x10);
163 5595 : if (outbody.data == NULL) {
164 0 : error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
165 0 : if (!NT_STATUS_IS_OK(error)) {
166 0 : smbd_server_connection_terminate(req->xconn,
167 : nt_errstr(error));
168 0 : return;
169 : }
170 0 : return;
171 : }
172 :
173 5595 : SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
174 :
175 5595 : SSVAL(outbody.data, 0x00, 0x10); /* struct size */
176 5595 : SCVAL(outbody.data, 0x02,
177 : out_share_type); /* share type */
178 5595 : SCVAL(outbody.data, 0x03, 0); /* reserved */
179 5595 : SIVAL(outbody.data, 0x04,
180 : out_share_flags); /* share flags */
181 5595 : SIVAL(outbody.data, 0x08,
182 : out_capabilities); /* capabilities */
183 5595 : SIVAL(outbody.data, 0x0C,
184 : out_maximal_access); /* maximal access */
185 :
186 5595 : error = smbd_smb2_request_done(req, outbody, NULL);
187 5595 : if (!NT_STATUS_IS_OK(error)) {
188 0 : smbd_server_connection_terminate(req->xconn,
189 : nt_errstr(error));
190 0 : return;
191 : }
192 : }
193 :
194 5609 : static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
195 : const char *in_path,
196 : uint8_t *out_share_type,
197 : uint32_t *out_share_flags,
198 : uint32_t *out_capabilities,
199 : uint32_t *out_maximal_access,
200 : uint32_t *out_tree_id,
201 : bool *disconnect)
202 : {
203 3800 : const struct loadparm_substitution *lp_sub =
204 1809 : loadparm_s3_global_substitution();
205 5609 : struct smbXsrv_connection *conn = req->xconn;
206 5609 : struct smbXsrv_session *session = req->session;
207 5609 : struct auth_session_info *session_info =
208 5609 : session->global->auth_session_info;
209 5609 : const char *share = in_path;
210 5609 : char *service = NULL;
211 5609 : int snum = -1;
212 : struct smbXsrv_tcon *tcon;
213 5609 : NTTIME now = timeval_to_nttime(&req->request_time);
214 5609 : connection_struct *compat_conn = NULL;
215 : NTSTATUS status;
216 5609 : bool encryption_desired = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED;
217 5609 : bool encryption_required = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED;
218 5609 : bool guest_session = false;
219 5609 : bool require_signed_tcon = false;
220 : uint32_t session_global_id;
221 5609 : char *share_name = NULL;
222 5609 : uint8_t encryption_flags = 0;
223 :
224 5609 : *disconnect = false;
225 :
226 5609 : if (strncmp(share, "\\\\", 2) == 0) {
227 5609 : const char *p = strchr(share+2, '\\');
228 5609 : if (p) {
229 5609 : share = p + 1;
230 : }
231 : }
232 :
233 5609 : DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
234 : in_path, share));
235 :
236 5609 : if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
237 493 : guest_session = true;
238 : }
239 :
240 5609 : if (conn->protocol >= PROTOCOL_SMB3_11 && !guest_session) {
241 2805 : require_signed_tcon = true;
242 : }
243 :
244 5609 : if (require_signed_tcon && !req->do_encryption && !req->do_signing) {
245 0 : DEBUG(1, ("smbd_smb2_tree_connect: reject request to share "
246 : "[%s] as '%s\\%s' without encryption or signing. "
247 : "Disconnecting.\n",
248 : share,
249 : req->session->global->auth_session_info->info->domain_name,
250 : req->session->global->auth_session_info->info->account_name));
251 0 : *disconnect = true;
252 0 : return NT_STATUS_ACCESS_DENIED;
253 : }
254 :
255 5609 : service = talloc_strdup(talloc_tos(), share);
256 5609 : if(!service) {
257 0 : return NT_STATUS_NO_MEMORY;
258 : }
259 :
260 5609 : if (!strlower_m(service)) {
261 0 : DEBUG(2, ("strlower_m %s failed\n", service));
262 0 : return NT_STATUS_INVALID_PARAMETER;
263 : }
264 :
265 : /* TODO: do more things... */
266 5609 : if (strequal(service,HOMES_NAME)) {
267 0 : if (session->homes_snum == -1) {
268 0 : DEBUG(2, ("[homes] share not available for "
269 : "user %s because it was not found "
270 : "or created at session setup "
271 : "time\n",
272 : session_info->unix_info->unix_name));
273 0 : return NT_STATUS_BAD_NETWORK_NAME;
274 : }
275 0 : snum = session->homes_snum;
276 5609 : } else if ((session->homes_snum != -1)
277 4 : && strequal(service,
278 4 : lp_servicename(talloc_tos(), lp_sub, session->homes_snum))) {
279 0 : snum = session->homes_snum;
280 : } else {
281 5609 : snum = find_service(talloc_tos(), service, &service);
282 5609 : if (!service) {
283 0 : return NT_STATUS_NO_MEMORY;
284 : }
285 : }
286 :
287 5609 : if (snum < 0) {
288 2 : DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
289 : service));
290 2 : return NT_STATUS_BAD_NETWORK_NAME;
291 : }
292 :
293 : /* Handle non-DFS clients attempting connections to msdfs proxy */
294 5607 : if (lp_host_msdfs()) {
295 5607 : char *proxy = lp_msdfs_proxy(talloc_tos(), lp_sub, snum);
296 :
297 5607 : if ((proxy != NULL) && (*proxy != '\0')) {
298 0 : DBG_NOTICE("refusing connection to dfs proxy share "
299 : "'%s' (pointing to %s)\n",
300 : service,
301 : proxy);
302 0 : TALLOC_FREE(proxy);
303 0 : return NT_STATUS_BAD_NETWORK_NAME;
304 : }
305 5607 : TALLOC_FREE(proxy);
306 : }
307 :
308 5607 : if ((lp_server_smb_encrypt(snum) >= SMB_ENCRYPTION_DESIRED) &&
309 0 : (conn->smb2.server.cipher != 0))
310 : {
311 0 : encryption_desired = true;
312 : }
313 :
314 5607 : if (lp_server_smb_encrypt(snum) == SMB_ENCRYPTION_REQUIRED) {
315 0 : encryption_desired = true;
316 0 : encryption_required = true;
317 : }
318 :
319 5607 : if (guest_session && encryption_required) {
320 0 : DEBUG(1,("reject guest as encryption is required for service %s\n",
321 : service));
322 0 : return NT_STATUS_ACCESS_DENIED;
323 : }
324 :
325 5607 : if (conn->smb2.server.cipher == 0) {
326 2358 : if (encryption_required) {
327 0 : DEBUG(1,("reject tcon with dialect[0x%04X] "
328 : "as encryption is required for service %s\n",
329 : conn->smb2.server.dialect, service));
330 0 : return NT_STATUS_ACCESS_DENIED;
331 : }
332 : }
333 :
334 5607 : if (encryption_desired) {
335 0 : encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED;
336 : }
337 5607 : if (encryption_required) {
338 0 : encryption_flags |= SMBXSRV_ENCRYPTION_REQUIRED;
339 : }
340 :
341 5607 : session_global_id = req->session->global->session_global_id;
342 5607 : share_name = lp_servicename(talloc_tos(), lp_sub, snum);
343 5607 : if (share_name == NULL) {
344 0 : return NT_STATUS_NO_MEMORY;
345 : }
346 :
347 5607 : if ((lp_max_connections(snum) > 0)
348 0 : && (count_current_connections(lp_const_servicename(snum), true) >=
349 0 : lp_max_connections(snum))) {
350 :
351 0 : DBG_WARNING("Max connections (%d) exceeded for [%s][%s]\n",
352 : lp_max_connections(snum),
353 : lp_const_servicename(snum), share_name);
354 0 : TALLOC_FREE(share_name);
355 0 : return NT_STATUS_INSUFFICIENT_RESOURCES;
356 : }
357 :
358 : /* create a new tcon as child of the session */
359 5607 : status = smb2srv_tcon_create(req->session,
360 : session_global_id,
361 : encryption_flags,
362 : share_name,
363 : now, &tcon);
364 5607 : TALLOC_FREE(share_name);
365 5607 : if (!NT_STATUS_IS_OK(status)) {
366 0 : return status;
367 : }
368 :
369 5607 : compat_conn = make_connection_smb2(req,
370 : tcon, snum,
371 : "???",
372 : &status);
373 5607 : if (compat_conn == NULL) {
374 12 : TALLOC_FREE(tcon);
375 12 : return status;
376 : }
377 :
378 5595 : tcon->compat = talloc_move(tcon, &compat_conn);
379 :
380 5595 : tcon->status = NT_STATUS_OK;
381 :
382 5595 : if (IS_PRINT(tcon->compat)) {
383 0 : *out_share_type = SMB2_SHARE_TYPE_PRINT;
384 5595 : } else if (IS_IPC(tcon->compat)) {
385 4049 : *out_share_type = SMB2_SHARE_TYPE_PIPE;
386 : } else {
387 1546 : *out_share_type = SMB2_SHARE_TYPE_DISK;
388 : }
389 :
390 5595 : *out_share_flags = 0;
391 :
392 5595 : if (lp_msdfs_root(SNUM(tcon->compat)) && lp_host_msdfs()) {
393 44 : *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT);
394 44 : *out_capabilities = SMB2_SHARE_CAP_DFS;
395 : } else {
396 5551 : *out_capabilities = 0;
397 : }
398 :
399 5595 : switch(lp_csc_policy(SNUM(tcon->compat))) {
400 5595 : case CSC_POLICY_MANUAL:
401 5595 : break;
402 0 : case CSC_POLICY_DOCUMENTS:
403 0 : *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING;
404 0 : break;
405 0 : case CSC_POLICY_PROGRAMS:
406 0 : *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING;
407 0 : break;
408 0 : case CSC_POLICY_DISABLE:
409 0 : *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING;
410 0 : break;
411 0 : default:
412 0 : break;
413 : }
414 :
415 11190 : if (lp_hide_unreadable(SNUM(tcon->compat)) ||
416 5595 : lp_hide_unwriteable_files(SNUM(tcon->compat))) {
417 0 : *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM;
418 : }
419 :
420 5595 : if (encryption_desired) {
421 0 : *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA;
422 : }
423 :
424 5595 : *out_maximal_access = tcon->compat->share_access;
425 :
426 5595 : *out_tree_id = tcon->global->tcon_wire_id;
427 5595 : req->last_tid = tcon->global->tcon_wire_id;
428 :
429 5595 : return NT_STATUS_OK;
430 : }
431 :
432 : struct smbd_smb2_tree_connect_state {
433 : const char *in_path;
434 : uint8_t out_share_type;
435 : uint32_t out_share_flags;
436 : uint32_t out_capabilities;
437 : uint32_t out_maximal_access;
438 : uint32_t out_tree_id;
439 : bool disconnect;
440 : };
441 :
442 5609 : static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx,
443 : struct tevent_context *ev,
444 : struct smbd_smb2_request *smb2req,
445 : uint16_t in_flags,
446 : const char *in_path)
447 : {
448 : struct tevent_req *req;
449 : struct smbd_smb2_tree_connect_state *state;
450 : NTSTATUS status;
451 :
452 5609 : req = tevent_req_create(mem_ctx, &state,
453 : struct smbd_smb2_tree_connect_state);
454 5609 : if (req == NULL) {
455 0 : return NULL;
456 : }
457 5609 : state->in_path = in_path;
458 :
459 28409 : status = smbd_smb2_tree_connect(smb2req,
460 5609 : state->in_path,
461 5609 : &state->out_share_type,
462 5609 : &state->out_share_flags,
463 5609 : &state->out_capabilities,
464 5609 : &state->out_maximal_access,
465 5609 : &state->out_tree_id,
466 5609 : &state->disconnect);
467 5609 : if (tevent_req_nterror(req, status)) {
468 14 : return tevent_req_post(req, ev);
469 : }
470 :
471 5595 : tevent_req_done(req);
472 5595 : return tevent_req_post(req, ev);
473 : }
474 :
475 5609 : static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req,
476 : uint8_t *out_share_type,
477 : uint32_t *out_share_flags,
478 : uint32_t *out_capabilities,
479 : uint32_t *out_maximal_access,
480 : uint32_t *out_tree_id,
481 : bool *disconnect)
482 : {
483 3800 : struct smbd_smb2_tree_connect_state *state =
484 5609 : tevent_req_data(req,
485 : struct smbd_smb2_tree_connect_state);
486 : NTSTATUS status;
487 :
488 5609 : if (tevent_req_is_nterror(req, &status)) {
489 14 : tevent_req_received(req);
490 14 : return status;
491 : }
492 :
493 5595 : *out_share_type = state->out_share_type;
494 5595 : *out_share_flags = state->out_share_flags;
495 5595 : *out_capabilities = state->out_capabilities;
496 5595 : *out_maximal_access = state->out_maximal_access;
497 5595 : *out_tree_id = state->out_tree_id;
498 5595 : *disconnect = state->disconnect;
499 :
500 5595 : tevent_req_received(req);
501 5595 : return NT_STATUS_OK;
502 : }
503 :
504 : static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
505 : struct tevent_context *ev,
506 : struct smbd_smb2_request *smb2req);
507 : static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req);
508 : static void smbd_smb2_request_tdis_done(struct tevent_req *subreq);
509 :
510 2401 : NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
511 : {
512 : NTSTATUS status;
513 2401 : struct tevent_req *subreq = NULL;
514 :
515 2401 : status = smbd_smb2_request_verify_sizes(req, 0x04);
516 2401 : if (!NT_STATUS_IS_OK(status)) {
517 0 : return smbd_smb2_request_error(req, status);
518 : }
519 :
520 2401 : subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req);
521 2401 : if (subreq == NULL) {
522 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
523 : }
524 2401 : tevent_req_set_callback(subreq, smbd_smb2_request_tdis_done, req);
525 :
526 : /*
527 : * Avoid sending a STATUS_PENDING message, it's very likely
528 : * the client won't expect that.
529 : */
530 2401 : return smbd_smb2_request_pending_queue(req, subreq, 0);
531 : }
532 :
533 2401 : static void smbd_smb2_request_tdis_done(struct tevent_req *subreq)
534 : {
535 1505 : struct smbd_smb2_request *smb2req =
536 2401 : tevent_req_callback_data(subreq,
537 : struct smbd_smb2_request);
538 : DATA_BLOB outbody;
539 : NTSTATUS status;
540 : NTSTATUS error;
541 :
542 2401 : status = smbd_smb2_tdis_recv(subreq);
543 2401 : TALLOC_FREE(subreq);
544 2401 : if (!NT_STATUS_IS_OK(status)) {
545 0 : error = smbd_smb2_request_error(smb2req, status);
546 0 : if (!NT_STATUS_IS_OK(error)) {
547 0 : smbd_server_connection_terminate(smb2req->xconn,
548 : nt_errstr(error));
549 0 : return;
550 : }
551 0 : return;
552 : }
553 :
554 2401 : outbody = smbd_smb2_generate_outbody(smb2req, 0x04);
555 2401 : if (outbody.data == NULL) {
556 0 : error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY);
557 0 : if (!NT_STATUS_IS_OK(error)) {
558 0 : smbd_server_connection_terminate(smb2req->xconn,
559 : nt_errstr(error));
560 0 : return;
561 : }
562 0 : return;
563 : }
564 :
565 2401 : SSVAL(outbody.data, 0x00, 0x04); /* struct size */
566 2401 : SSVAL(outbody.data, 0x02, 0); /* reserved */
567 :
568 2401 : error = smbd_smb2_request_done(smb2req, outbody, NULL);
569 2401 : if (!NT_STATUS_IS_OK(error)) {
570 0 : smbd_server_connection_terminate(smb2req->xconn,
571 : nt_errstr(error));
572 0 : return;
573 : }
574 : }
575 :
576 : struct smbd_smb2_tdis_state {
577 : struct smbd_smb2_request *smb2req;
578 : struct tevent_queue *wait_queue;
579 : };
580 :
581 : static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq);
582 :
583 2401 : static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
584 : struct tevent_context *ev,
585 : struct smbd_smb2_request *smb2req)
586 : {
587 : struct tevent_req *req;
588 : struct smbd_smb2_tdis_state *state;
589 : struct tevent_req *subreq;
590 2401 : struct smbXsrv_connection *xconn = NULL;
591 :
592 2401 : req = tevent_req_create(mem_ctx, &state,
593 : struct smbd_smb2_tdis_state);
594 2401 : if (req == NULL) {
595 0 : return NULL;
596 : }
597 2401 : state->smb2req = smb2req;
598 :
599 2401 : state->wait_queue = tevent_queue_create(state, "tdis_wait_queue");
600 2401 : if (tevent_req_nomem(state->wait_queue, req)) {
601 0 : return tevent_req_post(req, ev);
602 : }
603 :
604 : /*
605 : * Make sure that no new request will be able to use this tcon.
606 : */
607 2401 : smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
608 :
609 2401 : xconn = smb2req->xconn->client->connections;
610 4802 : for (; xconn != NULL; xconn = xconn->next) {
611 : struct smbd_smb2_request *preq;
612 :
613 4802 : for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
614 2401 : if (preq == smb2req) {
615 : /* Can't cancel current request. */
616 2401 : continue;
617 : }
618 0 : if (preq->tcon != smb2req->tcon) {
619 : /* Request on different tcon. */
620 0 : continue;
621 : }
622 :
623 0 : if (preq->subreq != NULL) {
624 0 : tevent_req_cancel(preq->subreq);
625 : }
626 :
627 : /*
628 : * Now wait until the request is finished.
629 : *
630 : * We don't set a callback, as we just want to block the
631 : * wait queue and the talloc_free() of the request will
632 : * remove the item from the wait queue.
633 : */
634 0 : subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
635 0 : if (tevent_req_nomem(subreq, req)) {
636 0 : return tevent_req_post(req, ev);
637 : }
638 : }
639 : }
640 :
641 : /*
642 : * Now we add our own waiter to the end of the queue,
643 : * this way we get notified when all pending requests are finished
644 : * and send to the socket.
645 : */
646 2401 : subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
647 2401 : if (tevent_req_nomem(subreq, req)) {
648 0 : return tevent_req_post(req, ev);
649 : }
650 2401 : tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req);
651 :
652 2401 : return req;
653 : }
654 :
655 2401 : static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq)
656 : {
657 2401 : struct tevent_req *req = tevent_req_callback_data(
658 : subreq, struct tevent_req);
659 2401 : struct smbd_smb2_tdis_state *state = tevent_req_data(
660 : req, struct smbd_smb2_tdis_state);
661 : NTSTATUS status;
662 :
663 2401 : tevent_queue_wait_recv(subreq);
664 2401 : TALLOC_FREE(subreq);
665 :
666 : /*
667 : * As we've been awoken, we may have changed
668 : * uid in the meantime. Ensure we're still
669 : * root (SMB2_OP_TDIS has .as_root = true).
670 : */
671 2401 : change_to_root_user();
672 :
673 2401 : status = smbXsrv_tcon_disconnect(state->smb2req->tcon,
674 2401 : state->smb2req->tcon->compat->vuid);
675 2401 : if (tevent_req_nterror(req, status)) {
676 0 : return;
677 : }
678 :
679 : /* We did tear down the tcon. */
680 2401 : TALLOC_FREE(state->smb2req->tcon);
681 2401 : tevent_req_done(req);
682 : }
683 :
684 2401 : static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req)
685 : {
686 2401 : return tevent_req_simple_recv_ntstatus(req);
687 : }
|