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 39173 : NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
49 : {
50 39173 : struct smbXsrv_connection *xconn = req->xconn;
51 621 : const uint8_t *inbody;
52 621 : uint16_t in_flags;
53 621 : uint16_t in_path_offset;
54 621 : uint16_t in_path_length;
55 621 : DATA_BLOB in_path_buffer;
56 621 : char *in_path_string;
57 621 : size_t in_path_string_size;
58 621 : NTSTATUS status;
59 621 : bool ok;
60 621 : struct tevent_req *subreq;
61 :
62 39173 : status = smbd_smb2_request_verify_sizes(req, 0x09);
63 39173 : if (!NT_STATUS_IS_OK(status)) {
64 0 : return smbd_smb2_request_error(req, status);
65 : }
66 39173 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
67 :
68 39173 : if (xconn->protocol >= PROTOCOL_SMB3_11) {
69 36376 : in_flags = SVAL(inbody, 0x02);
70 : } else {
71 2775 : in_flags = 0;
72 : }
73 39173 : in_path_offset = SVAL(inbody, 0x04);
74 39173 : in_path_length = SVAL(inbody, 0x06);
75 :
76 39173 : 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 39173 : 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 39173 : in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
85 39173 : in_path_buffer.length = in_path_length;
86 :
87 39173 : ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
88 38552 : in_path_buffer.data,
89 : in_path_buffer.length,
90 : &in_path_string,
91 : &in_path_string_size);
92 39173 : if (!ok) {
93 0 : return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
94 : }
95 :
96 39173 : if (in_path_buffer.length == 0) {
97 0 : in_path_string_size = 0;
98 : }
99 :
100 39173 : 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 39794 : subreq = smbd_smb2_tree_connect_send(req,
105 39173 : req->sconn->ev_ctx,
106 : req,
107 : in_flags,
108 : in_path_string);
109 39173 : if (subreq == NULL) {
110 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
111 : }
112 39173 : 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 39173 : return smbd_smb2_request_pending_queue(req, subreq, 0);
119 : }
120 :
121 39173 : static void smbd_smb2_request_tcon_done(struct tevent_req *subreq)
122 : {
123 621 : struct smbd_smb2_request *req =
124 39173 : tevent_req_callback_data(subreq,
125 : struct smbd_smb2_request);
126 621 : uint8_t *outhdr;
127 621 : DATA_BLOB outbody;
128 39173 : uint8_t out_share_type = 0;
129 39173 : uint32_t out_share_flags = 0;
130 39173 : uint32_t out_capabilities = 0;
131 39173 : uint32_t out_maximal_access = 0;
132 39173 : uint32_t out_tree_id = 0;
133 39173 : bool disconnect = false;
134 621 : NTSTATUS status;
135 621 : NTSTATUS error;
136 :
137 39173 : 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 39173 : TALLOC_FREE(subreq);
145 39173 : if (!NT_STATUS_IS_OK(status)) {
146 98 : if (disconnect) {
147 0 : smbd_server_connection_terminate(req->xconn,
148 : nt_errstr(status));
149 98 : return;
150 : }
151 98 : error = smbd_smb2_request_error(req, status);
152 98 : if (!NT_STATUS_IS_OK(error)) {
153 0 : smbd_server_connection_terminate(req->xconn,
154 : nt_errstr(error));
155 0 : return;
156 : }
157 98 : return;
158 : }
159 :
160 39075 : outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
161 :
162 39075 : outbody = smbd_smb2_generate_outbody(req, 0x10);
163 39075 : 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 39075 : SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
174 :
175 39075 : SSVAL(outbody.data, 0x00, 0x10); /* struct size */
176 39075 : SCVAL(outbody.data, 0x02,
177 : out_share_type); /* share type */
178 39075 : SCVAL(outbody.data, 0x03, 0); /* reserved */
179 39075 : SIVAL(outbody.data, 0x04,
180 : out_share_flags); /* share flags */
181 39075 : SIVAL(outbody.data, 0x08,
182 : out_capabilities); /* capabilities */
183 39075 : SIVAL(outbody.data, 0x0C,
184 : out_maximal_access); /* maximal access */
185 :
186 39075 : error = smbd_smb2_request_done(req, outbody, NULL);
187 39075 : 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 39173 : 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 621 : const struct loadparm_substitution *lp_sub =
204 39173 : loadparm_s3_global_substitution();
205 39173 : struct smbXsrv_connection *conn = req->xconn;
206 39173 : struct smbXsrv_session *session = req->session;
207 39173 : struct auth_session_info *session_info =
208 39173 : session->global->auth_session_info;
209 39173 : const char *share = in_path;
210 39173 : char *service = NULL;
211 39173 : int snum = -1;
212 621 : struct smbXsrv_tcon *tcon;
213 39173 : NTTIME now = timeval_to_nttime(&req->request_time);
214 39173 : connection_struct *compat_conn = NULL;
215 621 : NTSTATUS status;
216 39173 : bool encryption_desired = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED;
217 39173 : bool encryption_required = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED;
218 39173 : bool guest_session = false;
219 39173 : bool require_signed_tcon = false;
220 621 : uint32_t session_global_id;
221 39173 : char *share_name = NULL;
222 39173 : uint8_t encryption_flags = 0;
223 :
224 39173 : *disconnect = false;
225 :
226 39173 : if (strncmp(share, "\\\\", 2) == 0) {
227 39173 : const char *p = strchr(share+2, '\\');
228 39173 : if (p) {
229 39173 : share = p + 1;
230 : }
231 : }
232 :
233 39173 : DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
234 : in_path, share));
235 :
236 39173 : if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
237 1013 : guest_session = true;
238 : }
239 :
240 39173 : if (conn->protocol >= PROTOCOL_SMB3_11 && !guest_session) {
241 35414 : require_signed_tcon = true;
242 : }
243 :
244 39149 : 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 39173 : service = talloc_strdup(talloc_tos(), share);
256 39173 : if(!service) {
257 0 : return NT_STATUS_NO_MEMORY;
258 : }
259 :
260 39173 : 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 39173 : 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 39173 : } else if ((session->homes_snum != -1)
277 20128 : && strequal(service,
278 20128 : lp_servicename(talloc_tos(), lp_sub, session->homes_snum))) {
279 2 : snum = session->homes_snum;
280 : } else {
281 39171 : snum = find_service(talloc_tos(), service, &service);
282 39171 : if (!service) {
283 0 : return NT_STATUS_NO_MEMORY;
284 : }
285 : }
286 :
287 39173 : if (snum < 0) {
288 28 : DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
289 : service));
290 28 : return NT_STATUS_BAD_NETWORK_NAME;
291 : }
292 :
293 : /* Handle non-DFS clients attempting connections to msdfs proxy */
294 39145 : if (lp_host_msdfs()) {
295 39145 : char *proxy = lp_msdfs_proxy(talloc_tos(), lp_sub, snum);
296 :
297 39145 : 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 39145 : TALLOC_FREE(proxy);
306 : }
307 :
308 39145 : if ((lp_server_smb_encrypt(snum) >= SMB_ENCRYPTION_DESIRED) &&
309 246 : (conn->smb2.server.cipher != 0))
310 : {
311 222 : encryption_desired = true;
312 : }
313 :
314 39145 : if (lp_server_smb_encrypt(snum) == SMB_ENCRYPTION_REQUIRED) {
315 220 : encryption_desired = true;
316 220 : encryption_required = true;
317 : }
318 :
319 39145 : 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 39145 : if (conn->smb2.server.cipher == 0) {
326 2983 : if (encryption_required) {
327 12 : DEBUG(1,("reject tcon with dialect[0x%04X] "
328 : "as encryption is required for service %s\n",
329 : conn->smb2.server.dialect, service));
330 12 : return NT_STATUS_ACCESS_DENIED;
331 : }
332 : }
333 :
334 39133 : if (encryption_desired) {
335 222 : encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED;
336 : }
337 39133 : if (encryption_required) {
338 208 : encryption_flags |= SMBXSRV_ENCRYPTION_REQUIRED;
339 : }
340 :
341 39133 : session_global_id = req->session->global->session_global_id;
342 39133 : share_name = lp_servicename(talloc_tos(), lp_sub, snum);
343 39133 : if (share_name == NULL) {
344 0 : return NT_STATUS_NO_MEMORY;
345 : }
346 :
347 39133 : 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 39133 : status = smb2srv_tcon_create(req->session,
360 : session_global_id,
361 : encryption_flags,
362 : share_name,
363 : now, &tcon);
364 39133 : TALLOC_FREE(share_name);
365 39133 : if (!NT_STATUS_IS_OK(status)) {
366 0 : return status;
367 : }
368 :
369 39133 : compat_conn = make_connection_smb2(req,
370 : tcon, snum,
371 : "???",
372 : &status);
373 39133 : if (compat_conn == NULL) {
374 58 : TALLOC_FREE(tcon);
375 58 : return status;
376 : }
377 :
378 39075 : tcon->compat = talloc_move(tcon, &compat_conn);
379 :
380 39075 : tcon->status = NT_STATUS_OK;
381 :
382 39075 : if (IS_PRINT(tcon->compat)) {
383 26 : *out_share_type = SMB2_SHARE_TYPE_PRINT;
384 39049 : } else if (IS_IPC(tcon->compat)) {
385 22588 : *out_share_type = SMB2_SHARE_TYPE_PIPE;
386 : } else {
387 16461 : *out_share_type = SMB2_SHARE_TYPE_DISK;
388 : }
389 :
390 39075 : *out_share_flags = 0;
391 :
392 39075 : if (lp_msdfs_root(SNUM(tcon->compat)) && lp_host_msdfs()) {
393 2616 : *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT);
394 2616 : *out_capabilities = SMB2_SHARE_CAP_DFS;
395 : } else {
396 36459 : *out_capabilities = 0;
397 : }
398 :
399 39075 : switch(lp_csc_policy(SNUM(tcon->compat))) {
400 38454 : case CSC_POLICY_MANUAL:
401 38454 : 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 78140 : if (lp_hide_unreadable(SNUM(tcon->compat)) ||
416 39065 : lp_hide_unwriteable_files(SNUM(tcon->compat))) {
417 20 : *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM;
418 : }
419 :
420 39075 : if (encryption_desired) {
421 222 : *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA;
422 : }
423 :
424 39075 : *out_maximal_access = tcon->compat->share_access;
425 :
426 39075 : *out_tree_id = tcon->global->tcon_wire_id;
427 39075 : req->last_tid = tcon->global->tcon_wire_id;
428 :
429 39075 : 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 39173 : 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 621 : struct tevent_req *req;
449 621 : struct smbd_smb2_tree_connect_state *state;
450 621 : NTSTATUS status;
451 :
452 39173 : req = tevent_req_create(mem_ctx, &state,
453 : struct smbd_smb2_tree_connect_state);
454 39173 : if (req == NULL) {
455 0 : return NULL;
456 : }
457 39173 : state->in_path = in_path;
458 :
459 39173 : status = smbd_smb2_tree_connect(smb2req,
460 38552 : state->in_path,
461 38552 : &state->out_share_type,
462 38552 : &state->out_share_flags,
463 38552 : &state->out_capabilities,
464 38552 : &state->out_maximal_access,
465 38552 : &state->out_tree_id,
466 38552 : &state->disconnect);
467 39173 : if (tevent_req_nterror(req, status)) {
468 98 : return tevent_req_post(req, ev);
469 : }
470 :
471 39075 : tevent_req_done(req);
472 39075 : return tevent_req_post(req, ev);
473 : }
474 :
475 39173 : 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 621 : struct smbd_smb2_tree_connect_state *state =
484 39173 : tevent_req_data(req,
485 : struct smbd_smb2_tree_connect_state);
486 621 : NTSTATUS status;
487 :
488 39173 : if (tevent_req_is_nterror(req, &status)) {
489 98 : tevent_req_received(req);
490 98 : return status;
491 : }
492 :
493 39075 : *out_share_type = state->out_share_type;
494 39075 : *out_share_flags = state->out_share_flags;
495 39075 : *out_capabilities = state->out_capabilities;
496 39075 : *out_maximal_access = state->out_maximal_access;
497 39075 : *out_tree_id = state->out_tree_id;
498 39075 : *disconnect = state->disconnect;
499 :
500 39075 : tevent_req_received(req);
501 39075 : 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 25493 : NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
511 : {
512 0 : NTSTATUS status;
513 25493 : struct tevent_req *subreq = NULL;
514 :
515 25493 : status = smbd_smb2_request_verify_sizes(req, 0x04);
516 25493 : if (!NT_STATUS_IS_OK(status)) {
517 0 : return smbd_smb2_request_error(req, status);
518 : }
519 :
520 25493 : subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req);
521 25493 : if (subreq == NULL) {
522 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
523 : }
524 25493 : 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 25493 : return smbd_smb2_request_pending_queue(req, subreq, 0);
531 : }
532 :
533 25493 : static void smbd_smb2_request_tdis_done(struct tevent_req *subreq)
534 : {
535 0 : struct smbd_smb2_request *smb2req =
536 25493 : tevent_req_callback_data(subreq,
537 : struct smbd_smb2_request);
538 0 : DATA_BLOB outbody;
539 0 : NTSTATUS status;
540 0 : NTSTATUS error;
541 :
542 25493 : status = smbd_smb2_tdis_recv(subreq);
543 25493 : TALLOC_FREE(subreq);
544 25493 : 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 25493 : outbody = smbd_smb2_generate_outbody(smb2req, 0x04);
555 25493 : 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 25493 : SSVAL(outbody.data, 0x00, 0x04); /* struct size */
566 25493 : SSVAL(outbody.data, 0x02, 0); /* reserved */
567 :
568 25493 : error = smbd_smb2_request_done(smb2req, outbody, NULL);
569 25493 : 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 25493 : 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 0 : struct tevent_req *req;
588 0 : struct smbd_smb2_tdis_state *state;
589 0 : struct tevent_req *subreq;
590 25493 : struct smbXsrv_connection *xconn = NULL;
591 :
592 25493 : req = tevent_req_create(mem_ctx, &state,
593 : struct smbd_smb2_tdis_state);
594 25493 : if (req == NULL) {
595 0 : return NULL;
596 : }
597 25493 : state->smb2req = smb2req;
598 :
599 25493 : state->wait_queue = tevent_queue_create(state, "tdis_wait_queue");
600 25493 : 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 25493 : smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
608 :
609 25493 : xconn = smb2req->xconn->client->connections;
610 50986 : for (; xconn != NULL; xconn = xconn->next) {
611 0 : struct smbd_smb2_request *preq;
612 :
613 50992 : for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
614 25499 : if (preq == smb2req) {
615 : /* Can't cancel current request. */
616 25493 : continue;
617 : }
618 6 : if (preq->tcon != smb2req->tcon) {
619 : /* Request on different tcon. */
620 0 : continue;
621 : }
622 :
623 6 : if (preq->subreq != NULL) {
624 6 : 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 6 : subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
635 6 : 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 25493 : subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
647 25493 : if (tevent_req_nomem(subreq, req)) {
648 0 : return tevent_req_post(req, ev);
649 : }
650 25493 : tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req);
651 :
652 25493 : return req;
653 : }
654 :
655 25493 : static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq)
656 : {
657 25493 : struct tevent_req *req = tevent_req_callback_data(
658 : subreq, struct tevent_req);
659 25493 : struct smbd_smb2_tdis_state *state = tevent_req_data(
660 : req, struct smbd_smb2_tdis_state);
661 0 : NTSTATUS status;
662 :
663 25493 : tevent_queue_wait_recv(subreq);
664 25493 : 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 25493 : change_to_root_user();
672 :
673 25493 : status = smbXsrv_tcon_disconnect(state->smb2req->tcon,
674 25493 : state->smb2req->tcon->compat->vuid);
675 25493 : if (tevent_req_nterror(req, status)) {
676 0 : return;
677 : }
678 :
679 : /* We did tear down the tcon. */
680 25493 : TALLOC_FREE(state->smb2req->tcon);
681 25493 : tevent_req_done(req);
682 : }
683 :
684 25493 : static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req)
685 : {
686 25493 : return tevent_req_simple_recv_ntstatus(req);
687 : }
|