2 Unix SMB/CIFS implementation.
5 Copyright (C) Stefan Metzmacher 2009
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.
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.
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/>.
22 #include "smbd/smbd.h"
23 #include "smbd/globals.h"
24 #include "../libcli/smb/smb_common.h"
25 #include "../libcli/security/security.h"
27 #include "lib/param/loadparm.h"
28 #include "../lib/util/tevent_ntstatus.h"
29 #include "smbd/smbXsrv_session.h"
32 #define DBGC_CLASS DBGC_SMB2
34 static struct tevent_req
*smbd_smb2_tree_connect_send(TALLOC_CTX
*mem_ctx
,
35 struct tevent_context
*ev
,
36 struct smbd_smb2_request
*smb2req
,
39 static NTSTATUS
smbd_smb2_tree_connect_recv(struct tevent_req
*req
,
40 uint8_t *out_share_type
,
41 uint32_t *out_share_flags
,
42 uint32_t *out_capabilities
,
43 uint32_t *out_maximal_access
,
44 uint32_t *out_tree_id
,
47 static void smbd_smb2_request_tcon_done(struct tevent_req
*subreq
);
49 NTSTATUS
smbd_smb2_request_process_tcon(struct smbd_smb2_request
*req
)
51 struct smbXsrv_connection
*xconn
= req
->xconn
;
52 const uint8_t *inbody
;
54 uint16_t in_path_offset
;
55 uint16_t in_path_length
;
56 DATA_BLOB in_path_buffer
;
58 size_t in_path_string_size
;
61 struct tevent_req
*subreq
;
63 status
= smbd_smb2_request_verify_sizes(req
, 0x09);
64 if (!NT_STATUS_IS_OK(status
)) {
65 return smbd_smb2_request_error(req
, status
);
67 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
69 if (xconn
->protocol
>= PROTOCOL_SMB3_11
) {
70 in_flags
= SVAL(inbody
, 0x02);
74 in_path_offset
= SVAL(inbody
, 0x04);
75 in_path_length
= SVAL(inbody
, 0x06);
77 if (in_path_offset
!= (SMB2_HDR_BODY
+ SMBD_SMB2_IN_BODY_LEN(req
))) {
78 return smbd_smb2_request_error(req
, NT_STATUS_INVALID_PARAMETER
);
81 if (in_path_length
> SMBD_SMB2_IN_DYN_LEN(req
)) {
82 return smbd_smb2_request_error(req
, NT_STATUS_INVALID_PARAMETER
);
85 in_path_buffer
.data
= SMBD_SMB2_IN_DYN_PTR(req
);
86 in_path_buffer
.length
= in_path_length
;
88 ok
= convert_string_talloc(req
, CH_UTF16
, CH_UNIX
,
90 in_path_buffer
.length
,
92 &in_path_string_size
);
94 return smbd_smb2_request_error(req
, NT_STATUS_ILLEGAL_CHARACTER
);
97 if (in_path_buffer
.length
== 0) {
98 in_path_string_size
= 0;
101 if (strlen(in_path_string
) != in_path_string_size
) {
102 return smbd_smb2_request_error(req
, NT_STATUS_BAD_NETWORK_NAME
);
105 subreq
= smbd_smb2_tree_connect_send(req
,
110 if (subreq
== NULL
) {
111 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
113 tevent_req_set_callback(subreq
, smbd_smb2_request_tcon_done
, req
);
116 * Avoid sending a STATUS_PENDING message, it's very likely
117 * the client won't expect that.
119 return smbd_smb2_request_pending_queue(req
, subreq
, 0);
122 static void smbd_smb2_request_tcon_done(struct tevent_req
*subreq
)
124 struct smbd_smb2_request
*req
=
125 tevent_req_callback_data(subreq
,
126 struct smbd_smb2_request
);
129 uint8_t out_share_type
= 0;
130 uint32_t out_share_flags
= 0;
131 uint32_t out_capabilities
= 0;
132 uint32_t out_maximal_access
= 0;
133 uint32_t out_tree_id
= 0;
134 bool disconnect
= false;
138 status
= smbd_smb2_tree_connect_recv(subreq
,
146 if (!NT_STATUS_IS_OK(status
)) {
148 smbd_server_connection_terminate(req
->xconn
,
152 error
= smbd_smb2_request_error(req
, status
);
153 if (!NT_STATUS_IS_OK(error
)) {
154 smbd_server_connection_terminate(req
->xconn
,
161 outhdr
= SMBD_SMB2_OUT_HDR_PTR(req
);
163 outbody
= smbd_smb2_generate_outbody(req
, 0x10);
164 if (outbody
.data
== NULL
) {
165 error
= smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
166 if (!NT_STATUS_IS_OK(error
)) {
167 smbd_server_connection_terminate(req
->xconn
,
174 SIVAL(outhdr
, SMB2_HDR_TID
, out_tree_id
);
176 SSVAL(outbody
.data
, 0x00, 0x10); /* struct size */
177 SCVAL(outbody
.data
, 0x02,
178 out_share_type
); /* share type */
179 SCVAL(outbody
.data
, 0x03, 0); /* reserved */
180 SIVAL(outbody
.data
, 0x04,
181 out_share_flags
); /* share flags */
182 SIVAL(outbody
.data
, 0x08,
183 out_capabilities
); /* capabilities */
184 SIVAL(outbody
.data
, 0x0C,
185 out_maximal_access
); /* maximal access */
187 error
= smbd_smb2_request_done(req
, outbody
, NULL
);
188 if (!NT_STATUS_IS_OK(error
)) {
189 smbd_server_connection_terminate(req
->xconn
,
195 static NTSTATUS
smbd_smb2_tree_connect(struct smbd_smb2_request
*req
,
197 uint8_t *out_share_type
,
198 uint32_t *out_share_flags
,
199 uint32_t *out_capabilities
,
200 uint32_t *out_maximal_access
,
201 uint32_t *out_tree_id
,
204 const struct loadparm_substitution
*lp_sub
=
205 loadparm_s3_global_substitution();
206 struct smbXsrv_connection
*conn
= req
->xconn
;
207 struct smbXsrv_session
*session
= req
->session
;
208 struct auth_session_info
*session_info
=
209 session
->global
->auth_session_info
;
210 const char *share
= in_path
;
211 char *service
= NULL
;
213 struct smbXsrv_tcon
*tcon
;
214 NTTIME now
= timeval_to_nttime(&req
->request_time
);
215 connection_struct
*compat_conn
= NULL
;
217 bool encryption_desired
= req
->session
->global
->encryption_flags
& SMBXSRV_ENCRYPTION_DESIRED
;
218 bool encryption_required
= req
->session
->global
->encryption_flags
& SMBXSRV_ENCRYPTION_REQUIRED
;
219 bool guest_session
= false;
220 bool require_signed_tcon
= false;
221 uint32_t session_global_id
;
222 char *share_name
= NULL
;
223 uint8_t encryption_flags
= 0;
227 if (strncmp(share
, "\\\\", 2) == 0) {
228 const char *p
= strchr(share
+2, '\\');
234 DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
237 if (security_session_user_level(session_info
, NULL
) < SECURITY_USER
) {
238 guest_session
= true;
241 if (conn
->protocol
>= PROTOCOL_SMB3_11
&& !guest_session
) {
242 require_signed_tcon
= true;
245 if (require_signed_tcon
&& !req
->do_encryption
&& !req
->do_signing
) {
246 DEBUG(1, ("smbd_smb2_tree_connect: reject request to share "
247 "[%s] as '%s\\%s' without encryption or signing. "
250 req
->session
->global
->auth_session_info
->info
->domain_name
,
251 req
->session
->global
->auth_session_info
->info
->account_name
));
253 return NT_STATUS_ACCESS_DENIED
;
256 service
= talloc_strdup(talloc_tos(), share
);
258 return NT_STATUS_NO_MEMORY
;
261 if (!strlower_m(service
)) {
262 DEBUG(2, ("strlower_m %s failed\n", service
));
263 return NT_STATUS_INVALID_PARAMETER
;
266 /* TODO: do more things... */
267 if (strequal(service
,HOMES_NAME
)) {
268 if (session
->homes_snum
== -1) {
269 DEBUG(2, ("[homes] share not available for "
270 "user %s because it was not found "
271 "or created at session setup "
273 session_info
->unix_info
->unix_name
));
274 return NT_STATUS_BAD_NETWORK_NAME
;
276 snum
= session
->homes_snum
;
277 } else if ((session
->homes_snum
!= -1)
279 lp_servicename(talloc_tos(), lp_sub
, session
->homes_snum
))) {
280 snum
= session
->homes_snum
;
282 snum
= find_service(talloc_tos(), service
, &service
);
284 return NT_STATUS_NO_MEMORY
;
289 DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
291 return NT_STATUS_BAD_NETWORK_NAME
;
294 /* Handle non-DFS clients attempting connections to msdfs proxy */
295 if (lp_host_msdfs()) {
296 char *proxy
= lp_msdfs_proxy(talloc_tos(), lp_sub
, snum
);
298 if ((proxy
!= NULL
) && (*proxy
!= '\0')) {
299 DBG_NOTICE("refusing connection to dfs proxy share "
300 "'%s' (pointing to %s)\n",
304 return NT_STATUS_BAD_NETWORK_NAME
;
309 if ((lp_server_smb_encrypt(snum
) >= SMB_ENCRYPTION_DESIRED
) &&
310 (conn
->smb2
.server
.cipher
!= 0))
312 encryption_desired
= true;
315 if (lp_server_smb_encrypt(snum
) == SMB_ENCRYPTION_REQUIRED
) {
316 encryption_desired
= true;
317 encryption_required
= true;
320 if (guest_session
&& encryption_required
) {
321 DEBUG(1,("reject guest as encryption is required for service %s\n",
323 return NT_STATUS_ACCESS_DENIED
;
326 if (conn
->smb2
.server
.cipher
== 0) {
327 if (encryption_required
) {
328 DEBUG(1,("reject tcon with dialect[0x%04X] "
329 "as encryption is required for service %s\n",
330 conn
->smb2
.server
.dialect
, service
));
331 return NT_STATUS_ACCESS_DENIED
;
336 /* make sure we don't ask for optional encryption */
337 encryption_desired
= false;
339 if (encryption_desired
) {
340 encryption_flags
|= SMBXSRV_ENCRYPTION_DESIRED
;
342 if (encryption_required
) {
343 encryption_flags
|= SMBXSRV_ENCRYPTION_REQUIRED
;
346 session_global_id
= req
->session
->global
->session_global_id
;
347 share_name
= lp_servicename(talloc_tos(), lp_sub
, snum
);
348 if (share_name
== NULL
) {
349 return NT_STATUS_NO_MEMORY
;
352 if ((lp_max_connections(snum
) > 0)
353 && (count_current_connections(lp_const_servicename(snum
), true) >=
354 lp_max_connections(snum
))) {
356 DBG_WARNING("Max connections (%d) exceeded for [%s][%s]\n",
357 lp_max_connections(snum
),
358 lp_const_servicename(snum
), share_name
);
359 TALLOC_FREE(share_name
);
360 return NT_STATUS_INSUFFICIENT_RESOURCES
;
363 /* create a new tcon as child of the session */
364 status
= smb2srv_tcon_create(req
->session
,
369 TALLOC_FREE(share_name
);
370 if (!NT_STATUS_IS_OK(status
)) {
374 compat_conn
= make_connection_smb2(req
,
378 if (compat_conn
== NULL
) {
383 tcon
->compat
= talloc_move(tcon
, &compat_conn
);
385 tcon
->status
= NT_STATUS_OK
;
387 if (IS_PRINT(tcon
->compat
)) {
388 *out_share_type
= SMB2_SHARE_TYPE_PRINT
;
389 } else if (IS_IPC(tcon
->compat
)) {
390 *out_share_type
= SMB2_SHARE_TYPE_PIPE
;
392 *out_share_type
= SMB2_SHARE_TYPE_DISK
;
395 *out_share_flags
= 0;
397 if (lp_msdfs_root(SNUM(tcon
->compat
)) && lp_host_msdfs()) {
398 *out_share_flags
|= (SMB2_SHAREFLAG_DFS
|SMB2_SHAREFLAG_DFS_ROOT
);
399 *out_capabilities
= SMB2_SHARE_CAP_DFS
;
401 *out_capabilities
= 0;
404 switch(lp_csc_policy(SNUM(tcon
->compat
))) {
405 case CSC_POLICY_MANUAL
:
407 case CSC_POLICY_DOCUMENTS
:
408 *out_share_flags
|= SMB2_SHAREFLAG_AUTO_CACHING
;
410 case CSC_POLICY_PROGRAMS
:
411 *out_share_flags
|= SMB2_SHAREFLAG_VDO_CACHING
;
413 case CSC_POLICY_DISABLE
:
414 *out_share_flags
|= SMB2_SHAREFLAG_NO_CACHING
;
420 if (lp_hide_unreadable(SNUM(tcon
->compat
)) ||
421 lp_hide_unwriteable_files(SNUM(tcon
->compat
))) {
422 *out_share_flags
|= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM
;
425 if (encryption_desired
) {
426 *out_share_flags
|= SMB2_SHAREFLAG_ENCRYPT_DATA
;
430 * For disk shares we can change the client
431 * behavior on a cluster...
433 if (conn
->protocol
>= PROTOCOL_SMB3_00
&&
434 *out_share_type
== SMB2_SHARE_TYPE_DISK
)
436 bool persistent
= false; /* persistent handles not implemented yet */
437 bool cluster
= lp_clustering();
438 bool scaleout
= cluster
;
439 bool witness
= cluster
&& !lp_rpc_start_on_demand_helpers();
440 bool asymmetric
= false; /* shares are symmetric by default */
444 * In a ctdb cluster shares are continuously available,
445 * but windows clients mix this with the global persistent
448 * Persistent handles are requested if
449 * SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is present
450 * even without SMB2_CAP_PERSISTENT_HANDLES.
452 * And SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is
453 * required for SMB2_SHARE_CAP_CLUSTER to have
456 * So we better don't announce this by default
457 * until we support persistent handles.
459 announce
= lp_parm_bool(SNUM(tcon
->compat
),
461 "CONTINUOUS AVAILABILITY",
464 *out_capabilities
|= SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY
;
468 * ctdb clusters are always scale out...
470 announce
= lp_parm_bool(SNUM(tcon
->compat
),
475 *out_capabilities
|= SMB2_SHARE_CAP_SCALEOUT
;
479 * We support the witness service when ctdb is active
481 announce
= lp_parm_bool(SNUM(tcon
->compat
),
486 *out_capabilities
|= SMB2_SHARE_CAP_CLUSTER
;
490 * Shares in a ctdb cluster are symmetric by design.
492 * But it might be useful to let the client use
493 * an isolated transport and witness registration for the
496 if (conn
->protocol
>= PROTOCOL_SMB3_02
) {
497 announce
= lp_parm_bool(SNUM(tcon
->compat
),
503 *out_capabilities
|= SMB2_SHARE_CAP_ASYMMETRIC
;
507 *out_maximal_access
= tcon
->compat
->share_access
;
509 *out_tree_id
= tcon
->global
->tcon_wire_id
;
510 req
->last_tid
= tcon
->global
->tcon_wire_id
;
515 struct smbd_smb2_tree_connect_state
{
517 uint8_t out_share_type
;
518 uint32_t out_share_flags
;
519 uint32_t out_capabilities
;
520 uint32_t out_maximal_access
;
521 uint32_t out_tree_id
;
525 static struct tevent_req
*smbd_smb2_tree_connect_send(TALLOC_CTX
*mem_ctx
,
526 struct tevent_context
*ev
,
527 struct smbd_smb2_request
*smb2req
,
531 struct tevent_req
*req
;
532 struct smbd_smb2_tree_connect_state
*state
;
535 req
= tevent_req_create(mem_ctx
, &state
,
536 struct smbd_smb2_tree_connect_state
);
540 state
->in_path
= in_path
;
542 status
= smbd_smb2_tree_connect(smb2req
,
544 &state
->out_share_type
,
545 &state
->out_share_flags
,
546 &state
->out_capabilities
,
547 &state
->out_maximal_access
,
550 if (tevent_req_nterror(req
, status
)) {
551 return tevent_req_post(req
, ev
);
554 tevent_req_done(req
);
555 return tevent_req_post(req
, ev
);
558 static NTSTATUS
smbd_smb2_tree_connect_recv(struct tevent_req
*req
,
559 uint8_t *out_share_type
,
560 uint32_t *out_share_flags
,
561 uint32_t *out_capabilities
,
562 uint32_t *out_maximal_access
,
563 uint32_t *out_tree_id
,
566 struct smbd_smb2_tree_connect_state
*state
=
568 struct smbd_smb2_tree_connect_state
);
571 if (tevent_req_is_nterror(req
, &status
)) {
572 tevent_req_received(req
);
576 *out_share_type
= state
->out_share_type
;
577 *out_share_flags
= state
->out_share_flags
;
578 *out_capabilities
= state
->out_capabilities
;
579 *out_maximal_access
= state
->out_maximal_access
;
580 *out_tree_id
= state
->out_tree_id
;
581 *disconnect
= state
->disconnect
;
583 tevent_req_received(req
);
587 static struct tevent_req
*smbd_smb2_tdis_send(TALLOC_CTX
*mem_ctx
,
588 struct tevent_context
*ev
,
589 struct smbd_smb2_request
*smb2req
);
590 static NTSTATUS
smbd_smb2_tdis_recv(struct tevent_req
*req
);
591 static void smbd_smb2_request_tdis_done(struct tevent_req
*subreq
);
593 NTSTATUS
smbd_smb2_request_process_tdis(struct smbd_smb2_request
*req
)
596 struct tevent_req
*subreq
= NULL
;
598 status
= smbd_smb2_request_verify_sizes(req
, 0x04);
599 if (!NT_STATUS_IS_OK(status
)) {
600 return smbd_smb2_request_error(req
, status
);
603 subreq
= smbd_smb2_tdis_send(req
, req
->sconn
->ev_ctx
, req
);
604 if (subreq
== NULL
) {
605 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
607 tevent_req_set_callback(subreq
, smbd_smb2_request_tdis_done
, req
);
610 * Avoid sending a STATUS_PENDING message, it's very likely
611 * the client won't expect that.
613 return smbd_smb2_request_pending_queue(req
, subreq
, 0);
616 static void smbd_smb2_request_tdis_done(struct tevent_req
*subreq
)
618 struct smbd_smb2_request
*smb2req
=
619 tevent_req_callback_data(subreq
,
620 struct smbd_smb2_request
);
625 status
= smbd_smb2_tdis_recv(subreq
);
627 if (!NT_STATUS_IS_OK(status
)) {
628 error
= smbd_smb2_request_error(smb2req
, status
);
629 if (!NT_STATUS_IS_OK(error
)) {
630 smbd_server_connection_terminate(smb2req
->xconn
,
637 outbody
= smbd_smb2_generate_outbody(smb2req
, 0x04);
638 if (outbody
.data
== NULL
) {
639 error
= smbd_smb2_request_error(smb2req
, NT_STATUS_NO_MEMORY
);
640 if (!NT_STATUS_IS_OK(error
)) {
641 smbd_server_connection_terminate(smb2req
->xconn
,
648 SSVAL(outbody
.data
, 0x00, 0x04); /* struct size */
649 SSVAL(outbody
.data
, 0x02, 0); /* reserved */
651 error
= smbd_smb2_request_done(smb2req
, outbody
, NULL
);
652 if (!NT_STATUS_IS_OK(error
)) {
653 smbd_server_connection_terminate(smb2req
->xconn
,
659 struct smbd_smb2_tdis_state
{
660 struct smbd_smb2_request
*smb2req
;
661 struct tevent_queue
*wait_queue
;
664 static void smbd_smb2_tdis_wait_done(struct tevent_req
*subreq
);
666 struct check_for_lease_break_fsp_cmp_state
{
667 struct smbXsrv_tcon
*tcon
;
670 static bool check_for_lease_break_fsp_cmp_fn(struct files_struct
*fsp
,
673 struct check_for_lease_break_fsp_cmp_state
*state
=
674 (struct check_for_lease_break_fsp_cmp_state
*)private_data
;
676 return (fsp
->conn
== state
->tcon
->compat
);
679 static struct tevent_req
*smbd_smb2_tdis_send(TALLOC_CTX
*mem_ctx
,
680 struct tevent_context
*ev
,
681 struct smbd_smb2_request
*smb2req
)
683 struct tevent_req
*req
;
684 struct smbd_smb2_tdis_state
*state
;
685 struct tevent_req
*subreq
;
686 struct smbXsrv_connection
*xconn
= NULL
;
687 struct check_for_lease_break_fsp_cmp_state fsp_cmp_state
;
689 req
= tevent_req_create(mem_ctx
, &state
,
690 struct smbd_smb2_tdis_state
);
694 state
->smb2req
= smb2req
;
696 state
->wait_queue
= tevent_queue_create(state
, "tdis_wait_queue");
697 if (tevent_req_nomem(state
->wait_queue
, req
)) {
698 return tevent_req_post(req
, ev
);
702 * Make sure that no new request will be able to use this tcon.
704 smb2req
->tcon
->status
= NT_STATUS_NETWORK_NAME_DELETED
;
706 xconn
= smb2req
->xconn
->client
->connections
;
707 for (; xconn
!= NULL
; xconn
= xconn
->next
) {
708 struct smbd_smb2_request
*preq
;
710 for (preq
= xconn
->smb2
.requests
; preq
!= NULL
; preq
= preq
->next
) {
711 if (preq
== smb2req
) {
712 /* Can't cancel current request. */
715 if (preq
->tcon
!= smb2req
->tcon
) {
716 /* Request on different tcon. */
720 if (preq
->subreq
!= NULL
) {
721 tevent_req_cancel(preq
->subreq
);
725 * Now wait until the request is finished.
727 * We don't set a callback, as we just want to block the
728 * wait queue and the talloc_free() of the request will
729 * remove the item from the wait queue.
731 subreq
= tevent_queue_wait_send(preq
, ev
, state
->wait_queue
);
732 if (tevent_req_nomem(subreq
, req
)) {
733 return tevent_req_post(req
, ev
);
738 fsp_cmp_state
= (struct check_for_lease_break_fsp_cmp_state
) {
739 .tcon
= smb2req
->tcon
,
742 smbXsrv_wait_for_handle_lease_break(req
,
744 smb2req
->xconn
->client
,
746 check_for_lease_break_fsp_cmp_fn
,
748 if (!tevent_req_is_in_progress(req
)) {
749 return tevent_req_post(req
, ev
);
753 * Now we add our own waiter to the end of the queue,
754 * this way we get notified when all pending requests are finished
755 * and send to the socket.
757 subreq
= tevent_queue_wait_send(state
, ev
, state
->wait_queue
);
758 if (tevent_req_nomem(subreq
, req
)) {
759 return tevent_req_post(req
, ev
);
761 tevent_req_set_callback(subreq
, smbd_smb2_tdis_wait_done
, req
);
766 static void smbd_smb2_tdis_wait_done(struct tevent_req
*subreq
)
768 struct tevent_req
*req
= tevent_req_callback_data(
769 subreq
, struct tevent_req
);
770 struct smbd_smb2_tdis_state
*state
= tevent_req_data(
771 req
, struct smbd_smb2_tdis_state
);
774 tevent_queue_wait_recv(subreq
);
778 * As we've been awoken, we may have changed
779 * uid in the meantime. Ensure we're still
780 * root (SMB2_OP_TDIS has .as_root = true).
782 change_to_root_user();
784 status
= smbXsrv_tcon_disconnect(state
->smb2req
->tcon
,
785 state
->smb2req
->tcon
->compat
->vuid
);
786 if (tevent_req_nterror(req
, status
)) {
790 /* We did tear down the tcon. */
791 TALLOC_FREE(state
->smb2req
->tcon
);
792 tevent_req_done(req
);
795 static NTSTATUS
smbd_smb2_tdis_recv(struct tevent_req
*req
)
797 return tevent_req_simple_recv_ntstatus(req
);