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 39550 : NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
49 : {
50 39550 : 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 39550 : status = smbd_smb2_request_verify_sizes(req, 0x09);
63 39550 : if (!NT_STATUS_IS_OK(status)) {
64 0 : return smbd_smb2_request_error(req, status);
65 : }
66 39550 : inbody = SMBD_SMB2_IN_BODY_PTR(req);
67 :
68 39550 : if (xconn->protocol >= PROTOCOL_SMB3_11) {
69 36739 : in_flags = SVAL(inbody, 0x02);
70 : } else {
71 2789 : in_flags = 0;
72 : }
73 39550 : in_path_offset = SVAL(inbody, 0x04);
74 39550 : in_path_length = SVAL(inbody, 0x06);
75 :
76 39550 : 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 39550 : 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 39550 : in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req);
85 39550 : in_path_buffer.length = in_path_length;
86 :
87 39550 : ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
88 38929 : in_path_buffer.data,
89 : in_path_buffer.length,
90 : &in_path_string,
91 : &in_path_string_size);
92 39550 : if (!ok) {
93 0 : return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
94 : }
95 :
96 39550 : if (in_path_buffer.length == 0) {
97 0 : in_path_string_size = 0;
98 : }
99 :
100 39550 : 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 40171 : subreq = smbd_smb2_tree_connect_send(req,
105 39550 : req->sconn->ev_ctx,
106 : req,
107 : in_flags,
108 : in_path_string);
109 39550 : if (subreq == NULL) {
110 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
111 : }
112 39550 : 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 39550 : return smbd_smb2_request_pending_queue(req, subreq, 0);
119 : }
120 :
121 39550 : static void smbd_smb2_request_tcon_done(struct tevent_req *subreq)
122 : {
123 621 : struct smbd_smb2_request *req =
124 39550 : tevent_req_callback_data(subreq,
125 : struct smbd_smb2_request);
126 621 : uint8_t *outhdr;
127 621 : DATA_BLOB outbody;
128 39550 : uint8_t out_share_type = 0;
129 39550 : uint32_t out_share_flags = 0;
130 39550 : uint32_t out_capabilities = 0;
131 39550 : uint32_t out_maximal_access = 0;
132 39550 : uint32_t out_tree_id = 0;
133 39550 : bool disconnect = false;
134 621 : NTSTATUS status;
135 621 : NTSTATUS error;
136 :
137 39550 : 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 39550 : TALLOC_FREE(subreq);
145 39550 : 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 39452 : outhdr = SMBD_SMB2_OUT_HDR_PTR(req);
161 :
162 39452 : outbody = smbd_smb2_generate_outbody(req, 0x10);
163 39452 : 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 39452 : SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
174 :
175 39452 : SSVAL(outbody.data, 0x00, 0x10); /* struct size */
176 39452 : SCVAL(outbody.data, 0x02,
177 : out_share_type); /* share type */
178 39452 : SCVAL(outbody.data, 0x03, 0); /* reserved */
179 39452 : SIVAL(outbody.data, 0x04,
180 : out_share_flags); /* share flags */
181 39452 : SIVAL(outbody.data, 0x08,
182 : out_capabilities); /* capabilities */
183 39452 : SIVAL(outbody.data, 0x0C,
184 : out_maximal_access); /* maximal access */
185 :
186 39452 : error = smbd_smb2_request_done(req, outbody, NULL);
187 39452 : 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 39550 : 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 39550 : loadparm_s3_global_substitution();
205 39550 : struct smbXsrv_connection *conn = req->xconn;
206 39550 : struct smbXsrv_session *session = req->session;
207 39550 : struct auth_session_info *session_info =
208 39550 : session->global->auth_session_info;
209 39550 : const char *share = in_path;
210 39550 : char *service = NULL;
211 39550 : int snum = -1;
212 621 : struct smbXsrv_tcon *tcon;
213 39550 : NTTIME now = timeval_to_nttime(&req->request_time);
214 39550 : connection_struct *compat_conn = NULL;
215 621 : NTSTATUS status;
216 39550 : bool encryption_desired = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED;
217 39550 : bool encryption_required = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED;
218 39550 : bool guest_session = false;
219 39550 : bool require_signed_tcon = false;
220 621 : uint32_t session_global_id;
221 39550 : char *share_name = NULL;
222 39550 : uint8_t encryption_flags = 0;
223 :
224 39550 : *disconnect = false;
225 :
226 39550 : if (strncmp(share, "\\\\", 2) == 0) {
227 39550 : const char *p = strchr(share+2, '\\');
228 39550 : if (p) {
229 39550 : share = p + 1;
230 : }
231 : }
232 :
233 39550 : DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
234 : in_path, share));
235 :
236 39550 : if (security_session_user_level(session_info, NULL) < SECURITY_USER) {
237 1151 : guest_session = true;
238 : }
239 :
240 39550 : if (conn->protocol >= PROTOCOL_SMB3_11 && !guest_session) {
241 35647 : require_signed_tcon = true;
242 : }
243 :
244 39526 : 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 39550 : service = talloc_strdup(talloc_tos(), share);
256 39550 : if(!service) {
257 0 : return NT_STATUS_NO_MEMORY;
258 : }
259 :
260 39550 : 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 39550 : 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 39550 : } else if ((session->homes_snum != -1)
277 20432 : && strequal(service,
278 20432 : lp_servicename(talloc_tos(), lp_sub, session->homes_snum))) {
279 2 : snum = session->homes_snum;
280 : } else {
281 39548 : snum = find_service(talloc_tos(), service, &service);
282 39548 : if (!service) {
283 0 : return NT_STATUS_NO_MEMORY;
284 : }
285 : }
286 :
287 39550 : 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 39522 : if (lp_host_msdfs()) {
295 39522 : char *proxy = lp_msdfs_proxy(talloc_tos(), lp_sub, snum);
296 :
297 39522 : 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 39522 : TALLOC_FREE(proxy);
306 : }
307 :
308 39522 : 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 39522 : if (lp_server_smb_encrypt(snum) == SMB_ENCRYPTION_REQUIRED) {
315 220 : encryption_desired = true;
316 220 : encryption_required = true;
317 : }
318 :
319 39522 : 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 39522 : if (conn->smb2.server.cipher == 0) {
326 2997 : 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 39510 : if (encryption_desired) {
335 222 : encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED;
336 : }
337 39510 : if (encryption_required) {
338 208 : encryption_flags |= SMBXSRV_ENCRYPTION_REQUIRED;
339 : }
340 :
341 39510 : session_global_id = req->session->global->session_global_id;
342 39510 : share_name = lp_servicename(talloc_tos(), lp_sub, snum);
343 39510 : if (share_name == NULL) {
344 0 : return NT_STATUS_NO_MEMORY;
345 : }
346 :
347 39510 : 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 39510 : status = smb2srv_tcon_create(req->session,
360 : session_global_id,
361 : encryption_flags,
362 : share_name,
363 : now, &tcon);
364 39510 : TALLOC_FREE(share_name);
365 39510 : if (!NT_STATUS_IS_OK(status)) {
366 0 : return status;
367 : }
368 :
369 39510 : compat_conn = make_connection_smb2(req,
370 : tcon, snum,
371 : "???",
372 : &status);
373 39510 : if (compat_conn == NULL) {
374 58 : TALLOC_FREE(tcon);
375 58 : return status;
376 : }
377 :
378 39452 : tcon->compat = talloc_move(tcon, &compat_conn);
379 :
380 39452 : tcon->status = NT_STATUS_OK;
381 :
382 39452 : if (IS_PRINT(tcon->compat)) {
383 26 : *out_share_type = SMB2_SHARE_TYPE_PRINT;
384 39426 : } else if (IS_IPC(tcon->compat)) {
385 22794 : *out_share_type = SMB2_SHARE_TYPE_PIPE;
386 : } else {
387 16632 : *out_share_type = SMB2_SHARE_TYPE_DISK;
388 : }
389 :
390 39452 : *out_share_flags = 0;
391 :
392 39452 : 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 36836 : *out_capabilities = 0;
397 : }
398 :
399 39452 : switch(lp_csc_policy(SNUM(tcon->compat))) {
400 38831 : case CSC_POLICY_MANUAL:
401 38831 : 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 78894 : if (lp_hide_unreadable(SNUM(tcon->compat)) ||
416 39442 : lp_hide_unwriteable_files(SNUM(tcon->compat))) {
417 20 : *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM;
418 : }
419 :
420 39452 : if (encryption_desired) {
421 222 : *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA;
422 : }
423 :
424 : /*
425 : * For disk shares we can change the client
426 : * behavior on a cluster...
427 : */
428 39452 : if (conn->protocol >= PROTOCOL_SMB3_00 &&
429 36741 : *out_share_type == SMB2_SHARE_TYPE_DISK)
430 : {
431 15735 : bool persistent = false; /* persistent handles not implemented yet */
432 15735 : bool cluster = lp_clustering();
433 15735 : bool scaleout = cluster;
434 15735 : bool witness = cluster && !lp_rpc_start_on_demand_helpers();
435 15735 : bool asymmetric = false; /* shares are symmetric by default */
436 217 : bool announce;
437 :
438 : /*
439 : * In a ctdb cluster shares are continuously available,
440 : * but windows clients mix this with the global persistent
441 : * handles support.
442 : *
443 : * Persistent handles are requested if
444 : * SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is present
445 : * even without SMB2_CAP_PERSISTENT_HANDLES.
446 : *
447 : * And SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is
448 : * required for SMB2_SHARE_CAP_CLUSTER to have
449 : * an effect.
450 : *
451 : * So we better don't announce this by default
452 : * until we support persistent handles.
453 : */
454 15735 : announce = lp_parm_bool(SNUM(tcon->compat),
455 : "smb3 share cap",
456 : "CONTINUOUS AVAILABILITY",
457 : persistent);
458 15735 : if (announce) {
459 0 : *out_capabilities |= SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
460 : }
461 :
462 : /*
463 : * ctdb clusters are always scale out...
464 : */
465 15735 : announce = lp_parm_bool(SNUM(tcon->compat),
466 : "smb3 share cap",
467 : "SCALE OUT",
468 : scaleout);
469 15735 : if (announce) {
470 0 : *out_capabilities |= SMB2_SHARE_CAP_SCALEOUT;
471 : }
472 :
473 : /*
474 : * We support the witness service when ctdb is active
475 : */
476 15735 : announce = lp_parm_bool(SNUM(tcon->compat),
477 : "smb3 share cap",
478 : "CLUSTER",
479 : witness);
480 15735 : if (announce) {
481 0 : *out_capabilities |= SMB2_SHARE_CAP_CLUSTER;
482 : }
483 :
484 : /*
485 : * Shares in a ctdb cluster are symmetric by design.
486 : *
487 : * But it might be useful to let the client use
488 : * an isolated transport and witness registration for the
489 : * specific share.
490 : */
491 15735 : if (conn->protocol >= PROTOCOL_SMB3_02) {
492 15735 : announce = lp_parm_bool(SNUM(tcon->compat),
493 : "smb3 share cap",
494 : "ASYMMETRIC",
495 : asymmetric);
496 : }
497 15735 : if (announce) {
498 0 : *out_capabilities |= SMB2_SHARE_CAP_ASYMMETRIC;
499 : }
500 : }
501 :
502 39452 : *out_maximal_access = tcon->compat->share_access;
503 :
504 39452 : *out_tree_id = tcon->global->tcon_wire_id;
505 39452 : req->last_tid = tcon->global->tcon_wire_id;
506 :
507 39452 : return NT_STATUS_OK;
508 : }
509 :
510 : struct smbd_smb2_tree_connect_state {
511 : const char *in_path;
512 : uint8_t out_share_type;
513 : uint32_t out_share_flags;
514 : uint32_t out_capabilities;
515 : uint32_t out_maximal_access;
516 : uint32_t out_tree_id;
517 : bool disconnect;
518 : };
519 :
520 39550 : static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx,
521 : struct tevent_context *ev,
522 : struct smbd_smb2_request *smb2req,
523 : uint16_t in_flags,
524 : const char *in_path)
525 : {
526 621 : struct tevent_req *req;
527 621 : struct smbd_smb2_tree_connect_state *state;
528 621 : NTSTATUS status;
529 :
530 39550 : req = tevent_req_create(mem_ctx, &state,
531 : struct smbd_smb2_tree_connect_state);
532 39550 : if (req == NULL) {
533 0 : return NULL;
534 : }
535 39550 : state->in_path = in_path;
536 :
537 39550 : status = smbd_smb2_tree_connect(smb2req,
538 38929 : state->in_path,
539 38929 : &state->out_share_type,
540 38929 : &state->out_share_flags,
541 38929 : &state->out_capabilities,
542 38929 : &state->out_maximal_access,
543 38929 : &state->out_tree_id,
544 38929 : &state->disconnect);
545 39550 : if (tevent_req_nterror(req, status)) {
546 98 : return tevent_req_post(req, ev);
547 : }
548 :
549 39452 : tevent_req_done(req);
550 39452 : return tevent_req_post(req, ev);
551 : }
552 :
553 39550 : static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req,
554 : uint8_t *out_share_type,
555 : uint32_t *out_share_flags,
556 : uint32_t *out_capabilities,
557 : uint32_t *out_maximal_access,
558 : uint32_t *out_tree_id,
559 : bool *disconnect)
560 : {
561 621 : struct smbd_smb2_tree_connect_state *state =
562 39550 : tevent_req_data(req,
563 : struct smbd_smb2_tree_connect_state);
564 621 : NTSTATUS status;
565 :
566 39550 : if (tevent_req_is_nterror(req, &status)) {
567 98 : tevent_req_received(req);
568 98 : return status;
569 : }
570 :
571 39452 : *out_share_type = state->out_share_type;
572 39452 : *out_share_flags = state->out_share_flags;
573 39452 : *out_capabilities = state->out_capabilities;
574 39452 : *out_maximal_access = state->out_maximal_access;
575 39452 : *out_tree_id = state->out_tree_id;
576 39452 : *disconnect = state->disconnect;
577 :
578 39452 : tevent_req_received(req);
579 39452 : return NT_STATUS_OK;
580 : }
581 :
582 : static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
583 : struct tevent_context *ev,
584 : struct smbd_smb2_request *smb2req);
585 : static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req);
586 : static void smbd_smb2_request_tdis_done(struct tevent_req *subreq);
587 :
588 25835 : NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
589 : {
590 0 : NTSTATUS status;
591 25835 : struct tevent_req *subreq = NULL;
592 :
593 25835 : status = smbd_smb2_request_verify_sizes(req, 0x04);
594 25835 : if (!NT_STATUS_IS_OK(status)) {
595 0 : return smbd_smb2_request_error(req, status);
596 : }
597 :
598 25835 : subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req);
599 25835 : if (subreq == NULL) {
600 0 : return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
601 : }
602 25835 : tevent_req_set_callback(subreq, smbd_smb2_request_tdis_done, req);
603 :
604 : /*
605 : * Avoid sending a STATUS_PENDING message, it's very likely
606 : * the client won't expect that.
607 : */
608 25835 : return smbd_smb2_request_pending_queue(req, subreq, 0);
609 : }
610 :
611 25835 : static void smbd_smb2_request_tdis_done(struct tevent_req *subreq)
612 : {
613 0 : struct smbd_smb2_request *smb2req =
614 25835 : tevent_req_callback_data(subreq,
615 : struct smbd_smb2_request);
616 0 : DATA_BLOB outbody;
617 0 : NTSTATUS status;
618 0 : NTSTATUS error;
619 :
620 25835 : status = smbd_smb2_tdis_recv(subreq);
621 25835 : TALLOC_FREE(subreq);
622 25835 : if (!NT_STATUS_IS_OK(status)) {
623 0 : error = smbd_smb2_request_error(smb2req, status);
624 0 : if (!NT_STATUS_IS_OK(error)) {
625 0 : smbd_server_connection_terminate(smb2req->xconn,
626 : nt_errstr(error));
627 0 : return;
628 : }
629 0 : return;
630 : }
631 :
632 25835 : outbody = smbd_smb2_generate_outbody(smb2req, 0x04);
633 25835 : if (outbody.data == NULL) {
634 0 : error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY);
635 0 : if (!NT_STATUS_IS_OK(error)) {
636 0 : smbd_server_connection_terminate(smb2req->xconn,
637 : nt_errstr(error));
638 0 : return;
639 : }
640 0 : return;
641 : }
642 :
643 25835 : SSVAL(outbody.data, 0x00, 0x04); /* struct size */
644 25835 : SSVAL(outbody.data, 0x02, 0); /* reserved */
645 :
646 25835 : error = smbd_smb2_request_done(smb2req, outbody, NULL);
647 25835 : if (!NT_STATUS_IS_OK(error)) {
648 0 : smbd_server_connection_terminate(smb2req->xconn,
649 : nt_errstr(error));
650 0 : return;
651 : }
652 : }
653 :
654 : struct smbd_smb2_tdis_state {
655 : struct smbd_smb2_request *smb2req;
656 : struct tevent_queue *wait_queue;
657 : };
658 :
659 : static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq);
660 :
661 25835 : static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx,
662 : struct tevent_context *ev,
663 : struct smbd_smb2_request *smb2req)
664 : {
665 0 : struct tevent_req *req;
666 0 : struct smbd_smb2_tdis_state *state;
667 0 : struct tevent_req *subreq;
668 25835 : struct smbXsrv_connection *xconn = NULL;
669 :
670 25835 : req = tevent_req_create(mem_ctx, &state,
671 : struct smbd_smb2_tdis_state);
672 25835 : if (req == NULL) {
673 0 : return NULL;
674 : }
675 25835 : state->smb2req = smb2req;
676 :
677 25835 : state->wait_queue = tevent_queue_create(state, "tdis_wait_queue");
678 25835 : if (tevent_req_nomem(state->wait_queue, req)) {
679 0 : return tevent_req_post(req, ev);
680 : }
681 :
682 : /*
683 : * Make sure that no new request will be able to use this tcon.
684 : */
685 25835 : smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
686 :
687 25835 : xconn = smb2req->xconn->client->connections;
688 51670 : for (; xconn != NULL; xconn = xconn->next) {
689 0 : struct smbd_smb2_request *preq;
690 :
691 51676 : for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) {
692 25841 : if (preq == smb2req) {
693 : /* Can't cancel current request. */
694 25835 : continue;
695 : }
696 6 : if (preq->tcon != smb2req->tcon) {
697 : /* Request on different tcon. */
698 0 : continue;
699 : }
700 :
701 6 : if (preq->subreq != NULL) {
702 6 : tevent_req_cancel(preq->subreq);
703 : }
704 :
705 : /*
706 : * Now wait until the request is finished.
707 : *
708 : * We don't set a callback, as we just want to block the
709 : * wait queue and the talloc_free() of the request will
710 : * remove the item from the wait queue.
711 : */
712 6 : subreq = tevent_queue_wait_send(preq, ev, state->wait_queue);
713 6 : if (tevent_req_nomem(subreq, req)) {
714 0 : return tevent_req_post(req, ev);
715 : }
716 : }
717 : }
718 :
719 : /*
720 : * Now we add our own waiter to the end of the queue,
721 : * this way we get notified when all pending requests are finished
722 : * and send to the socket.
723 : */
724 25835 : subreq = tevent_queue_wait_send(state, ev, state->wait_queue);
725 25835 : if (tevent_req_nomem(subreq, req)) {
726 0 : return tevent_req_post(req, ev);
727 : }
728 25835 : tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req);
729 :
730 25835 : return req;
731 : }
732 :
733 25835 : static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq)
734 : {
735 25835 : struct tevent_req *req = tevent_req_callback_data(
736 : subreq, struct tevent_req);
737 25835 : struct smbd_smb2_tdis_state *state = tevent_req_data(
738 : req, struct smbd_smb2_tdis_state);
739 0 : NTSTATUS status;
740 :
741 25835 : tevent_queue_wait_recv(subreq);
742 25835 : TALLOC_FREE(subreq);
743 :
744 : /*
745 : * As we've been awoken, we may have changed
746 : * uid in the meantime. Ensure we're still
747 : * root (SMB2_OP_TDIS has .as_root = true).
748 : */
749 25835 : change_to_root_user();
750 :
751 25835 : status = smbXsrv_tcon_disconnect(state->smb2req->tcon,
752 25835 : state->smb2req->tcon->compat->vuid);
753 25835 : if (tevent_req_nterror(req, status)) {
754 0 : return;
755 : }
756 :
757 : /* We did tear down the tcon. */
758 25835 : TALLOC_FREE(state->smb2req->tcon);
759 25835 : tevent_req_done(req);
760 : }
761 :
762 25835 : static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req)
763 : {
764 25835 : return tevent_req_simple_recv_ntstatus(req);
765 : }
|