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