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