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"
31 #define DBGC_CLASS DBGC_SMB2
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
,
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
,
45 static void smbd_smb2_request_tcon_done(struct tevent_req
*subreq
);
47 NTSTATUS
smbd_smb2_request_process_tcon(struct smbd_smb2_request
*req
)
49 const uint8_t *inbody
;
50 uint16_t in_path_offset
;
51 uint16_t in_path_length
;
52 DATA_BLOB in_path_buffer
;
54 size_t in_path_string_size
;
57 struct tevent_req
*subreq
;
59 status
= smbd_smb2_request_verify_sizes(req
, 0x09);
60 if (!NT_STATUS_IS_OK(status
)) {
61 return smbd_smb2_request_error(req
, status
);
63 inbody
= SMBD_SMB2_IN_BODY_PTR(req
);
65 in_path_offset
= SVAL(inbody
, 0x04);
66 in_path_length
= SVAL(inbody
, 0x06);
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
);
72 if (in_path_length
> SMBD_SMB2_IN_DYN_LEN(req
)) {
73 return smbd_smb2_request_error(req
, NT_STATUS_INVALID_PARAMETER
);
76 in_path_buffer
.data
= SMBD_SMB2_IN_DYN_PTR(req
);
77 in_path_buffer
.length
= in_path_length
;
79 ok
= convert_string_talloc(req
, CH_UTF16
, CH_UNIX
,
81 in_path_buffer
.length
,
83 &in_path_string_size
);
85 return smbd_smb2_request_error(req
, NT_STATUS_ILLEGAL_CHARACTER
);
88 if (in_path_buffer
.length
== 0) {
89 in_path_string_size
= 0;
92 if (strlen(in_path_string
) != in_path_string_size
) {
93 return smbd_smb2_request_error(req
, NT_STATUS_BAD_NETWORK_NAME
);
96 subreq
= smbd_smb2_tree_connect_send(req
,
100 if (subreq
== NULL
) {
101 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
103 tevent_req_set_callback(subreq
, smbd_smb2_request_tcon_done
, req
);
105 return smbd_smb2_request_pending_queue(req
, subreq
, 500);
108 static void smbd_smb2_request_tcon_done(struct tevent_req
*subreq
)
110 struct smbd_smb2_request
*req
=
111 tevent_req_callback_data(subreq
,
112 struct smbd_smb2_request
);
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;
124 status
= smbd_smb2_tree_connect_recv(subreq
,
132 if (!NT_STATUS_IS_OK(status
)) {
134 smbd_server_connection_terminate(req
->xconn
,
138 error
= smbd_smb2_request_error(req
, status
);
139 if (!NT_STATUS_IS_OK(error
)) {
140 smbd_server_connection_terminate(req
->xconn
,
147 outhdr
= SMBD_SMB2_OUT_HDR_PTR(req
);
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
,
160 SIVAL(outhdr
, SMB2_HDR_TID
, out_tree_id
);
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 */
173 error
= smbd_smb2_request_done(req
, outbody
, NULL
);
174 if (!NT_STATUS_IS_OK(error
)) {
175 smbd_server_connection_terminate(req
->xconn
,
181 static NTSTATUS
smbd_smb2_tree_connect(struct smbd_smb2_request
*req
,
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
,
190 struct smbXsrv_connection
*conn
= req
->xconn
;
191 const char *share
= in_path
;
192 char *service
= NULL
;
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
;
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;
206 if (strncmp(share
, "\\\\", 2) == 0) {
207 const char *p
= strchr(share
+2, '\\');
213 DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
216 if (security_session_user_level(compat_vuser
->session_info
, NULL
) < SECURITY_USER
) {
217 guest_session
= true;
220 if (conn
->protocol
>= PROTOCOL_SMB3_11
&& !guest_session
) {
221 require_signed_tcon
= true;
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. "
229 req
->session
->global
->auth_session_info
->info
->domain_name
,
230 req
->session
->global
->auth_session_info
->info
->account_name
));
232 return NT_STATUS_ACCESS_DENIED
;
235 service
= talloc_strdup(talloc_tos(), share
);
237 return NT_STATUS_NO_MEMORY
;
240 if (!strlower_m(service
)) {
241 DEBUG(2, ("strlower_m %s failed\n", service
));
242 return NT_STATUS_INVALID_PARAMETER
;
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 "
252 compat_vuser
->session_info
->unix_info
->unix_name
));
253 return NT_STATUS_BAD_NETWORK_NAME
;
255 snum
= compat_vuser
->homes_snum
;
256 } else if ((compat_vuser
->homes_snum
!= -1)
258 lp_servicename(talloc_tos(), compat_vuser
->homes_snum
))) {
259 snum
= compat_vuser
->homes_snum
;
261 snum
= find_service(talloc_tos(), service
, &service
);
263 return NT_STATUS_NO_MEMORY
;
268 DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
270 return NT_STATUS_BAD_NETWORK_NAME
;
273 /* Handle non-DFS clients attempting connections to msdfs proxy */
274 if (lp_host_msdfs()) {
275 char *proxy
= lp_msdfs_proxy(talloc_tos(), snum
);
277 if ((proxy
!= NULL
) && (*proxy
!= '\0')) {
278 DBG_NOTICE("refusing connection to dfs proxy share "
279 "'%s' (pointing to %s)\n",
283 return NT_STATUS_BAD_NETWORK_NAME
;
288 if ((lp_smb_encrypt(snum
) >= SMB_SIGNING_DESIRED
) &&
289 (conn
->smb2
.server
.cipher
!= 0))
291 encryption_desired
= true;
294 if (lp_smb_encrypt(snum
) == SMB_SIGNING_REQUIRED
) {
295 encryption_desired
= true;
296 encryption_required
= true;
299 if (guest_session
&& encryption_required
) {
300 DEBUG(1,("reject guest as encryption is required for service %s\n",
302 return NT_STATUS_ACCESS_DENIED
;
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
;
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
)) {
320 if (encryption_desired
) {
321 tcon
->global
->encryption_flags
|= SMBXSRV_ENCRYPTION_DESIRED
;
323 if (encryption_required
) {
324 tcon
->global
->encryption_flags
|= SMBXSRV_ENCRYPTION_REQUIRED
;
327 compat_conn
= make_connection_smb2(req
,
329 req
->session
->compat
,
332 if (compat_conn
== NULL
) {
337 tcon
->global
->share_name
= lp_servicename(tcon
->global
,
339 if (tcon
->global
->share_name
== NULL
) {
340 conn_free(compat_conn
);
342 return NT_STATUS_NO_MEMORY
;
344 tcon
->global
->session_global_id
=
345 req
->session
->global
->session_global_id
;
347 tcon
->compat
= talloc_move(tcon
, &compat_conn
);
349 tcon
->status
= NT_STATUS_OK
;
351 status
= smbXsrv_tcon_update(tcon
);
352 if (!NT_STATUS_IS_OK(status
)) {
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
;
362 *out_share_type
= SMB2_SHARE_TYPE_DISK
;
365 *out_share_flags
= 0;
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
;
371 *out_capabilities
= 0;
374 switch(lp_csc_policy(SNUM(tcon
->compat
))) {
375 case CSC_POLICY_MANUAL
:
377 case CSC_POLICY_DOCUMENTS
:
378 *out_share_flags
|= SMB2_SHAREFLAG_AUTO_CACHING
;
380 case CSC_POLICY_PROGRAMS
:
381 *out_share_flags
|= SMB2_SHAREFLAG_VDO_CACHING
;
383 case CSC_POLICY_DISABLE
:
384 *out_share_flags
|= SMB2_SHAREFLAG_NO_CACHING
;
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
;
395 if (encryption_desired
) {
396 *out_share_flags
|= SMB2_SHAREFLAG_ENCRYPT_DATA
;
399 *out_maximal_access
= tcon
->compat
->share_access
;
401 *out_tree_id
= tcon
->global
->tcon_wire_id
;
402 req
->last_tid
= tcon
->global
->tcon_wire_id
;
407 struct smbd_smb2_tree_connect_state
{
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
;
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
,
422 struct tevent_req
*req
;
423 struct smbd_smb2_tree_connect_state
*state
;
426 req
= tevent_req_create(mem_ctx
, &state
,
427 struct smbd_smb2_tree_connect_state
);
431 state
->in_path
= in_path
;
433 status
= smbd_smb2_tree_connect(smb2req
,
435 &state
->out_share_type
,
436 &state
->out_share_flags
,
437 &state
->out_capabilities
,
438 &state
->out_maximal_access
,
441 if (tevent_req_nterror(req
, status
)) {
442 return tevent_req_post(req
, ev
);
445 tevent_req_done(req
);
446 return tevent_req_post(req
, ev
);
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
,
457 struct smbd_smb2_tree_connect_state
*state
=
459 struct smbd_smb2_tree_connect_state
);
462 if (tevent_req_is_nterror(req
, &status
)) {
463 tevent_req_received(req
);
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
;
474 tevent_req_received(req
);
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
);
484 NTSTATUS
smbd_smb2_request_process_tdis(struct smbd_smb2_request
*req
)
487 struct tevent_req
*subreq
= NULL
;
489 status
= smbd_smb2_request_verify_sizes(req
, 0x04);
490 if (!NT_STATUS_IS_OK(status
)) {
491 return smbd_smb2_request_error(req
, status
);
494 subreq
= smbd_smb2_tdis_send(req
, req
->ev_ctx
, req
);
495 if (subreq
== NULL
) {
496 return smbd_smb2_request_error(req
, NT_STATUS_NO_MEMORY
);
498 tevent_req_set_callback(subreq
, smbd_smb2_request_tdis_done
, req
);
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.
504 return smbd_smb2_request_pending_queue(req
, subreq
, 10000000);
507 static void smbd_smb2_request_tdis_done(struct tevent_req
*subreq
)
509 struct smbd_smb2_request
*smb2req
=
510 tevent_req_callback_data(subreq
,
511 struct smbd_smb2_request
);
516 status
= smbd_smb2_tdis_recv(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
,
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
,
539 SSVAL(outbody
.data
, 0x00, 0x04); /* struct size */
540 SSVAL(outbody
.data
, 0x02, 0); /* reserved */
542 error
= smbd_smb2_request_done(smb2req
, outbody
, NULL
);
543 if (!NT_STATUS_IS_OK(error
)) {
544 smbd_server_connection_terminate(smb2req
->xconn
,
550 struct smbd_smb2_tdis_state
{
551 struct smbd_smb2_request
*smb2req
;
552 struct tevent_queue
*wait_queue
;
555 static void smbd_smb2_tdis_wait_done(struct tevent_req
*subreq
);
557 static struct tevent_req
*smbd_smb2_tdis_send(TALLOC_CTX
*mem_ctx
,
558 struct tevent_context
*ev
,
559 struct smbd_smb2_request
*smb2req
)
561 struct tevent_req
*req
;
562 struct smbd_smb2_tdis_state
*state
;
563 struct tevent_req
*subreq
;
564 struct smbXsrv_connection
*xconn
= NULL
;
566 req
= tevent_req_create(mem_ctx
, &state
,
567 struct smbd_smb2_tdis_state
);
571 state
->smb2req
= smb2req
;
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
);
579 * Make sure that no new request will be able to use this tcon.
581 smb2req
->tcon
->status
= NT_STATUS_NETWORK_NAME_DELETED
;
583 xconn
= smb2req
->xconn
->client
->connections
;
584 for (; xconn
!= NULL
; xconn
= xconn
->next
) {
585 struct smbd_smb2_request
*preq
;
587 for (preq
= xconn
->smb2
.requests
; preq
!= NULL
; preq
= preq
->next
) {
588 if (preq
== smb2req
) {
589 /* Can't cancel current request. */
592 if (preq
->tcon
!= smb2req
->tcon
) {
593 /* Request on different tcon. */
598 * Never cancel anything in a compound
599 * request. Way too hard to deal with
602 if (!preq
->compound_related
&& preq
->subreq
!= NULL
) {
603 tevent_req_cancel(preq
->subreq
);
607 * Now wait until the request is finished.
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.
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
);
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.
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
);
629 tevent_req_set_callback(subreq
, smbd_smb2_tdis_wait_done
, req
);
634 static void smbd_smb2_tdis_wait_done(struct tevent_req
*subreq
)
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
);
642 tevent_queue_wait_recv(subreq
);
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).
650 change_to_root_user();
652 status
= smbXsrv_tcon_disconnect(state
->smb2req
->tcon
,
653 state
->smb2req
->tcon
->compat
->vuid
);
654 if (tevent_req_nterror(req
, status
)) {
658 /* We did tear down the tcon. */
659 TALLOC_FREE(state
->smb2req
->tcon
);
660 tevent_req_done(req
);
663 static NTSTATUS
smbd_smb2_tdis_recv(struct tevent_req
*req
)
665 return tevent_req_simple_recv_ntstatus(req
);