/src/samba/source3/smbd/smb2_tcon.c
Line | Count | Source |
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 | | #include "smbd/smbXsrv_session.h" |
30 | | #include "lib/tsocket/tsocket.h" |
31 | | |
32 | | #undef DBGC_CLASS |
33 | 0 | #define DBGC_CLASS DBGC_SMB2 |
34 | | |
35 | | static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx, |
36 | | struct tevent_context *ev, |
37 | | struct smbd_smb2_request *smb2req, |
38 | | uint16_t in_flags, |
39 | | const char *in_path); |
40 | | static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req, |
41 | | uint8_t *out_share_type, |
42 | | uint32_t *out_share_flags, |
43 | | uint32_t *out_capabilities, |
44 | | uint32_t *out_maximal_access, |
45 | | uint32_t *out_tree_id, |
46 | | bool *disconnect); |
47 | | |
48 | | static void smbd_smb2_request_tcon_done(struct tevent_req *subreq); |
49 | | |
50 | | NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req) |
51 | 0 | { |
52 | 0 | struct smbXsrv_connection *xconn = req->xconn; |
53 | 0 | const uint8_t *inbody; |
54 | 0 | uint16_t in_flags; |
55 | 0 | uint16_t in_path_offset; |
56 | 0 | uint16_t in_path_length; |
57 | 0 | DATA_BLOB in_path_buffer; |
58 | 0 | char *in_path_string; |
59 | 0 | size_t in_path_string_size; |
60 | 0 | NTSTATUS status; |
61 | 0 | bool ok; |
62 | 0 | struct tevent_req *subreq; |
63 | |
|
64 | 0 | status = smbd_smb2_request_verify_sizes(req, 0x09); |
65 | 0 | if (!NT_STATUS_IS_OK(status)) { |
66 | 0 | return smbd_smb2_request_error(req, status); |
67 | 0 | } |
68 | 0 | inbody = SMBD_SMB2_IN_BODY_PTR(req); |
69 | |
|
70 | 0 | if (xconn->protocol >= PROTOCOL_SMB3_11) { |
71 | 0 | in_flags = SVAL(inbody, 0x02); |
72 | 0 | } else { |
73 | 0 | in_flags = 0; |
74 | 0 | } |
75 | 0 | in_path_offset = SVAL(inbody, 0x04); |
76 | 0 | in_path_length = SVAL(inbody, 0x06); |
77 | |
|
78 | 0 | if (in_path_offset != (SMB2_HDR_BODY + SMBD_SMB2_IN_BODY_LEN(req))) { |
79 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
80 | 0 | } |
81 | | |
82 | 0 | if (in_path_length > SMBD_SMB2_IN_DYN_LEN(req)) { |
83 | 0 | return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); |
84 | 0 | } |
85 | | |
86 | 0 | in_path_buffer.data = SMBD_SMB2_IN_DYN_PTR(req); |
87 | 0 | in_path_buffer.length = in_path_length; |
88 | |
|
89 | 0 | ok = convert_string_talloc(req, CH_UTF16, CH_UNIX, |
90 | 0 | in_path_buffer.data, |
91 | 0 | in_path_buffer.length, |
92 | 0 | &in_path_string, |
93 | 0 | &in_path_string_size); |
94 | 0 | if (!ok) { |
95 | 0 | return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER); |
96 | 0 | } |
97 | | |
98 | 0 | if (in_path_buffer.length == 0) { |
99 | 0 | in_path_string_size = 0; |
100 | 0 | } |
101 | |
|
102 | 0 | if (strlen(in_path_string) != in_path_string_size) { |
103 | 0 | return smbd_smb2_request_error(req, NT_STATUS_BAD_NETWORK_NAME); |
104 | 0 | } |
105 | | |
106 | 0 | subreq = smbd_smb2_tree_connect_send(req, |
107 | 0 | req->sconn->ev_ctx, |
108 | 0 | req, |
109 | 0 | in_flags, |
110 | 0 | in_path_string); |
111 | 0 | if (subreq == NULL) { |
112 | 0 | return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
113 | 0 | } |
114 | 0 | tevent_req_set_callback(subreq, smbd_smb2_request_tcon_done, req); |
115 | | |
116 | | /* |
117 | | * Avoid sending a STATUS_PENDING message, it's very likely |
118 | | * the client won't expect that. |
119 | | */ |
120 | 0 | return smbd_smb2_request_pending_queue(req, subreq, 0); |
121 | 0 | } |
122 | | |
123 | | static void smbd_smb2_request_tcon_done(struct tevent_req *subreq) |
124 | 0 | { |
125 | 0 | struct smbd_smb2_request *req = |
126 | 0 | tevent_req_callback_data(subreq, |
127 | 0 | struct smbd_smb2_request); |
128 | 0 | uint8_t *outhdr; |
129 | 0 | DATA_BLOB outbody; |
130 | 0 | uint8_t out_share_type = 0; |
131 | 0 | uint32_t out_share_flags = 0; |
132 | 0 | uint32_t out_capabilities = 0; |
133 | 0 | uint32_t out_maximal_access = 0; |
134 | 0 | uint32_t out_tree_id = 0; |
135 | 0 | bool disconnect = false; |
136 | 0 | NTSTATUS status; |
137 | 0 | NTSTATUS error; |
138 | |
|
139 | 0 | status = smbd_smb2_tree_connect_recv(subreq, |
140 | 0 | &out_share_type, |
141 | 0 | &out_share_flags, |
142 | 0 | &out_capabilities, |
143 | 0 | &out_maximal_access, |
144 | 0 | &out_tree_id, |
145 | 0 | &disconnect); |
146 | 0 | TALLOC_FREE(subreq); |
147 | 0 | if (!NT_STATUS_IS_OK(status)) { |
148 | 0 | if (disconnect) { |
149 | 0 | smbd_server_connection_terminate(req->xconn, |
150 | 0 | nt_errstr(status)); |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | error = smbd_smb2_request_error(req, status); |
154 | 0 | if (!NT_STATUS_IS_OK(error)) { |
155 | 0 | smbd_server_connection_terminate(req->xconn, |
156 | 0 | nt_errstr(error)); |
157 | 0 | return; |
158 | 0 | } |
159 | 0 | return; |
160 | 0 | } |
161 | | |
162 | 0 | outhdr = SMBD_SMB2_OUT_HDR_PTR(req); |
163 | |
|
164 | 0 | outbody = smbd_smb2_generate_outbody(req, 0x10); |
165 | 0 | if (outbody.data == NULL) { |
166 | 0 | error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
167 | 0 | if (!NT_STATUS_IS_OK(error)) { |
168 | 0 | smbd_server_connection_terminate(req->xconn, |
169 | 0 | nt_errstr(error)); |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | return; |
173 | 0 | } |
174 | | |
175 | 0 | SIVAL(outhdr, SMB2_HDR_TID, out_tree_id); |
176 | |
|
177 | 0 | SSVAL(outbody.data, 0x00, 0x10); /* struct size */ |
178 | 0 | SCVAL(outbody.data, 0x02, |
179 | 0 | out_share_type); /* share type */ |
180 | 0 | SCVAL(outbody.data, 0x03, 0); /* reserved */ |
181 | 0 | SIVAL(outbody.data, 0x04, |
182 | 0 | out_share_flags); /* share flags */ |
183 | 0 | SIVAL(outbody.data, 0x08, |
184 | 0 | out_capabilities); /* capabilities */ |
185 | 0 | SIVAL(outbody.data, 0x0C, |
186 | 0 | out_maximal_access); /* maximal access */ |
187 | |
|
188 | 0 | error = smbd_smb2_request_done(req, outbody, NULL); |
189 | 0 | if (!NT_STATUS_IS_OK(error)) { |
190 | 0 | smbd_server_connection_terminate(req->xconn, |
191 | 0 | nt_errstr(error)); |
192 | 0 | return; |
193 | 0 | } |
194 | 0 | } |
195 | | |
196 | | static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req, |
197 | | const char *in_path, |
198 | | uint8_t *out_share_type, |
199 | | uint32_t *out_share_flags, |
200 | | uint32_t *out_capabilities, |
201 | | uint32_t *out_maximal_access, |
202 | | uint32_t *out_tree_id, |
203 | | bool *disconnect) |
204 | 0 | { |
205 | 0 | const struct loadparm_substitution *lp_sub = |
206 | 0 | loadparm_s3_global_substitution(); |
207 | 0 | struct smbXsrv_connection *conn = req->xconn; |
208 | 0 | struct smbXsrv_session *session = req->session; |
209 | 0 | struct auth_session_info *session_info = |
210 | 0 | session->global->auth_session_info; |
211 | 0 | const char *share = in_path; |
212 | 0 | char *service = NULL; |
213 | 0 | int snum = -1; |
214 | 0 | struct smbXsrv_tcon *tcon; |
215 | 0 | NTTIME now = timeval_to_nttime(&req->request_time); |
216 | 0 | connection_struct *compat_conn = NULL; |
217 | 0 | NTSTATUS status; |
218 | 0 | bool encryption_desired = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_DESIRED; |
219 | 0 | bool encryption_required = req->session->global->encryption_flags & SMBXSRV_ENCRYPTION_REQUIRED; |
220 | 0 | bool guest_session = false; |
221 | 0 | bool require_signed_tcon = false; |
222 | 0 | uint32_t session_global_id; |
223 | 0 | char *share_name = NULL; |
224 | 0 | uint8_t encryption_flags = 0; |
225 | |
|
226 | 0 | *disconnect = false; |
227 | |
|
228 | 0 | if (strncmp(share, "\\\\", 2) == 0) { |
229 | 0 | const char *p = strchr(share+2, '\\'); |
230 | 0 | if (p) { |
231 | 0 | share = p + 1; |
232 | 0 | } |
233 | 0 | } |
234 | |
|
235 | 0 | DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n", |
236 | 0 | in_path, share)); |
237 | |
|
238 | 0 | if (security_session_user_level(session_info, NULL) < SECURITY_USER) { |
239 | 0 | guest_session = true; |
240 | 0 | } |
241 | |
|
242 | 0 | if (conn->protocol >= PROTOCOL_SMB3_11 && !guest_session) { |
243 | 0 | require_signed_tcon = true; |
244 | 0 | } |
245 | |
|
246 | 0 | if (require_signed_tcon && !req->do_encryption && !req->do_signing) { |
247 | 0 | DEBUG(1, ("smbd_smb2_tree_connect: reject request to share " |
248 | 0 | "[%s] as '%s\\%s' without encryption or signing. " |
249 | 0 | "Disconnecting.\n", |
250 | 0 | share, |
251 | 0 | req->session->global->auth_session_info->info->domain_name, |
252 | 0 | req->session->global->auth_session_info->info->account_name)); |
253 | 0 | *disconnect = true; |
254 | 0 | return NT_STATUS_ACCESS_DENIED; |
255 | 0 | } |
256 | | |
257 | 0 | service = talloc_strdup(talloc_tos(), share); |
258 | 0 | if(!service) { |
259 | 0 | return NT_STATUS_NO_MEMORY; |
260 | 0 | } |
261 | | |
262 | 0 | if (!strlower_m(service)) { |
263 | 0 | DEBUG(2, ("strlower_m %s failed\n", service)); |
264 | 0 | return NT_STATUS_INVALID_PARAMETER; |
265 | 0 | } |
266 | | |
267 | | /* TODO: do more things... */ |
268 | 0 | if (strequal(service,HOMES_NAME)) { |
269 | 0 | if (session->homes_snum == -1) { |
270 | 0 | DEBUG(2, ("[homes] share not available for " |
271 | 0 | "user %s because it was not found " |
272 | 0 | "or created at session setup " |
273 | 0 | "time\n", |
274 | 0 | session_info->unix_info->unix_name)); |
275 | 0 | return NT_STATUS_BAD_NETWORK_NAME; |
276 | 0 | } |
277 | 0 | snum = session->homes_snum; |
278 | 0 | } else if ((session->homes_snum != -1) |
279 | 0 | && strequal(service, |
280 | 0 | lp_servicename(talloc_tos(), lp_sub, session->homes_snum))) { |
281 | 0 | snum = session->homes_snum; |
282 | 0 | } else { |
283 | 0 | snum = find_service(talloc_tos(), service, &service); |
284 | 0 | if (!service) { |
285 | 0 | return NT_STATUS_NO_MEMORY; |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | 0 | if (snum < 0) { |
290 | 0 | DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n", |
291 | 0 | service)); |
292 | 0 | return NT_STATUS_BAD_NETWORK_NAME; |
293 | 0 | } |
294 | | |
295 | | /* Handle non-DFS clients attempting connections to msdfs proxy */ |
296 | 0 | if (lp_host_msdfs()) { |
297 | 0 | char *proxy = lp_msdfs_proxy(talloc_tos(), lp_sub, snum); |
298 | |
|
299 | 0 | if ((proxy != NULL) && (*proxy != '\0')) { |
300 | 0 | DBG_NOTICE("refusing connection to dfs proxy share " |
301 | 0 | "'%s' (pointing to %s)\n", |
302 | 0 | service, |
303 | 0 | proxy); |
304 | 0 | TALLOC_FREE(proxy); |
305 | 0 | return NT_STATUS_BAD_NETWORK_NAME; |
306 | 0 | } |
307 | 0 | TALLOC_FREE(proxy); |
308 | 0 | } |
309 | | |
310 | 0 | if ((lp_server_smb_encrypt(conn, snum) >= SMB_ENCRYPTION_DESIRED) && |
311 | 0 | (conn->smb2.server.cipher != 0)) |
312 | 0 | { |
313 | 0 | encryption_desired = true; |
314 | 0 | } |
315 | |
|
316 | 0 | if (lp_server_smb_encrypt(conn, snum) == SMB_ENCRYPTION_REQUIRED) { |
317 | 0 | encryption_desired = true; |
318 | 0 | encryption_required = true; |
319 | 0 | } |
320 | |
|
321 | 0 | if (guest_session && encryption_required) { |
322 | 0 | DEBUG(1,("reject guest as encryption is required for service %s\n", |
323 | 0 | service)); |
324 | 0 | return NT_STATUS_ACCESS_DENIED; |
325 | 0 | } |
326 | | |
327 | 0 | if (conn->smb2.server.cipher == 0) { |
328 | 0 | if (encryption_required) { |
329 | 0 | char *addr = tsocket_address_string(conn->remote_address, |
330 | 0 | talloc_tos()); |
331 | |
|
332 | 0 | DBG_WARNING("reject tcon from %s with dialect[0x%04X] " |
333 | 0 | "as encryption is required for service %s\n", |
334 | 0 | addr ? addr : "[UNKNOWN]", |
335 | 0 | conn->smb2.server.dialect, service); |
336 | |
|
337 | 0 | TALLOC_FREE(addr); |
338 | |
|
339 | 0 | return NT_STATUS_ACCESS_DENIED; |
340 | 0 | } |
341 | 0 | } |
342 | | |
343 | 0 | if (guest_session) { |
344 | | /* make sure we don't ask for optional encryption */ |
345 | 0 | encryption_desired = false; |
346 | 0 | } |
347 | 0 | if (encryption_desired) { |
348 | 0 | encryption_flags |= SMBXSRV_ENCRYPTION_DESIRED; |
349 | 0 | } |
350 | 0 | if (encryption_required) { |
351 | 0 | encryption_flags |= SMBXSRV_ENCRYPTION_REQUIRED; |
352 | 0 | } |
353 | |
|
354 | 0 | session_global_id = req->session->global->session_global_id; |
355 | 0 | share_name = lp_servicename(talloc_tos(), lp_sub, snum); |
356 | 0 | if (share_name == NULL) { |
357 | 0 | return NT_STATUS_NO_MEMORY; |
358 | 0 | } |
359 | | |
360 | 0 | if ((lp_max_connections(snum) > 0) |
361 | 0 | && (count_current_connections(lp_const_servicename(snum), true) >= |
362 | 0 | lp_max_connections(snum))) { |
363 | |
|
364 | 0 | DBG_WARNING("Max connections (%d) exceeded for [%s][%s]\n", |
365 | 0 | lp_max_connections(snum), |
366 | 0 | lp_const_servicename(snum), share_name); |
367 | 0 | TALLOC_FREE(share_name); |
368 | 0 | return NT_STATUS_INSUFFICIENT_RESOURCES; |
369 | 0 | } |
370 | | |
371 | | /* create a new tcon as child of the session */ |
372 | 0 | status = smb2srv_tcon_create(req->session, |
373 | 0 | session_global_id, |
374 | 0 | encryption_flags, |
375 | 0 | share_name, |
376 | 0 | now, &tcon); |
377 | 0 | TALLOC_FREE(share_name); |
378 | 0 | if (!NT_STATUS_IS_OK(status)) { |
379 | 0 | return status; |
380 | 0 | } |
381 | | |
382 | 0 | compat_conn = make_connection_smb2(req, |
383 | 0 | tcon, snum, |
384 | 0 | "???", |
385 | 0 | &status); |
386 | 0 | if (compat_conn == NULL) { |
387 | 0 | TALLOC_FREE(tcon); |
388 | 0 | return status; |
389 | 0 | } |
390 | | |
391 | 0 | tcon->compat = talloc_move(tcon, &compat_conn); |
392 | |
|
393 | 0 | tcon->status = NT_STATUS_OK; |
394 | |
|
395 | 0 | if (IS_PRINT(tcon->compat)) { |
396 | 0 | *out_share_type = SMB2_SHARE_TYPE_PRINT; |
397 | 0 | } else if (IS_IPC(tcon->compat)) { |
398 | 0 | *out_share_type = SMB2_SHARE_TYPE_PIPE; |
399 | 0 | } else { |
400 | 0 | *out_share_type = SMB2_SHARE_TYPE_DISK; |
401 | 0 | } |
402 | |
|
403 | 0 | *out_share_flags = 0; |
404 | |
|
405 | 0 | if (lp_msdfs_root(SNUM(tcon->compat)) && lp_host_msdfs()) { |
406 | 0 | *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT); |
407 | 0 | *out_capabilities = SMB2_SHARE_CAP_DFS; |
408 | 0 | } else { |
409 | 0 | *out_capabilities = 0; |
410 | 0 | } |
411 | |
|
412 | 0 | switch(lp_csc_policy(SNUM(tcon->compat))) { |
413 | 0 | case CSC_POLICY_MANUAL: |
414 | 0 | break; |
415 | 0 | case CSC_POLICY_DOCUMENTS: |
416 | 0 | *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING; |
417 | 0 | break; |
418 | 0 | case CSC_POLICY_PROGRAMS: |
419 | 0 | *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING; |
420 | 0 | break; |
421 | 0 | case CSC_POLICY_DISABLE: |
422 | 0 | *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING; |
423 | 0 | break; |
424 | 0 | default: |
425 | 0 | break; |
426 | 0 | } |
427 | | |
428 | 0 | if (lp_hide_unreadable(SNUM(tcon->compat)) || |
429 | 0 | lp_hide_unwriteable_files(SNUM(tcon->compat))) { |
430 | 0 | *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM; |
431 | 0 | } |
432 | |
|
433 | 0 | if (encryption_desired) { |
434 | 0 | *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA; |
435 | 0 | } |
436 | | |
437 | | /* |
438 | | * For disk shares we can change the client |
439 | | * behavior on a cluster... |
440 | | */ |
441 | 0 | if (conn->protocol >= PROTOCOL_SMB3_00 && |
442 | 0 | *out_share_type == SMB2_SHARE_TYPE_DISK) |
443 | 0 | { |
444 | 0 | bool persistent = false; /* persistent handles not implemented yet */ |
445 | 0 | bool cluster = lp_clustering(); |
446 | 0 | bool scaleout = cluster; |
447 | 0 | bool witness = cluster && !lp_rpc_start_on_demand_helpers(); |
448 | 0 | bool asymmetric = false; /* shares are symmetric by default */ |
449 | 0 | bool announce; |
450 | | |
451 | | /* |
452 | | * In a ctdb cluster shares are continuously available, |
453 | | * but windows clients mix this with the global persistent |
454 | | * handles support. |
455 | | * |
456 | | * Persistent handles are requested if |
457 | | * SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is present |
458 | | * even without SMB2_CAP_PERSISTENT_HANDLES. |
459 | | * |
460 | | * And SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY is |
461 | | * required for SMB2_SHARE_CAP_CLUSTER to have |
462 | | * an effect. |
463 | | * |
464 | | * So we better don't announce this by default |
465 | | * until we support persistent handles. |
466 | | */ |
467 | 0 | announce = lp_parm_bool(SNUM(tcon->compat), |
468 | 0 | "smb3 share cap", |
469 | 0 | "CONTINUOUS AVAILABILITY", |
470 | 0 | persistent); |
471 | 0 | if (announce) { |
472 | 0 | *out_capabilities |= SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY; |
473 | 0 | } |
474 | | |
475 | | /* |
476 | | * ctdb clusters are always scale out... |
477 | | */ |
478 | 0 | announce = lp_parm_bool(SNUM(tcon->compat), |
479 | 0 | "smb3 share cap", |
480 | 0 | "SCALE OUT", |
481 | 0 | scaleout); |
482 | 0 | if (announce) { |
483 | 0 | *out_capabilities |= SMB2_SHARE_CAP_SCALEOUT; |
484 | 0 | } |
485 | | |
486 | | /* |
487 | | * We support the witness service when ctdb is active |
488 | | */ |
489 | 0 | announce = lp_parm_bool(SNUM(tcon->compat), |
490 | 0 | "smb3 share cap", |
491 | 0 | "CLUSTER", |
492 | 0 | witness); |
493 | 0 | if (announce) { |
494 | 0 | *out_capabilities |= SMB2_SHARE_CAP_CLUSTER; |
495 | 0 | } |
496 | | |
497 | | /* |
498 | | * Shares in a ctdb cluster are symmetric by design. |
499 | | * |
500 | | * But it might be useful to let the client use |
501 | | * an isolated transport and witness registration for the |
502 | | * specific share. |
503 | | */ |
504 | 0 | if (conn->protocol >= PROTOCOL_SMB3_02) { |
505 | 0 | announce = lp_parm_bool(SNUM(tcon->compat), |
506 | 0 | "smb3 share cap", |
507 | 0 | "ASYMMETRIC", |
508 | 0 | asymmetric); |
509 | 0 | } |
510 | 0 | if (announce) { |
511 | 0 | *out_capabilities |= SMB2_SHARE_CAP_ASYMMETRIC; |
512 | 0 | } |
513 | 0 | } |
514 | |
|
515 | 0 | *out_maximal_access = tcon->compat->share_access; |
516 | |
|
517 | 0 | *out_tree_id = tcon->global->tcon_wire_id; |
518 | 0 | req->last_tid = tcon->global->tcon_wire_id; |
519 | |
|
520 | 0 | return NT_STATUS_OK; |
521 | 0 | } |
522 | | |
523 | | struct smbd_smb2_tree_connect_state { |
524 | | const char *in_path; |
525 | | uint8_t out_share_type; |
526 | | uint32_t out_share_flags; |
527 | | uint32_t out_capabilities; |
528 | | uint32_t out_maximal_access; |
529 | | uint32_t out_tree_id; |
530 | | bool disconnect; |
531 | | }; |
532 | | |
533 | | static struct tevent_req *smbd_smb2_tree_connect_send(TALLOC_CTX *mem_ctx, |
534 | | struct tevent_context *ev, |
535 | | struct smbd_smb2_request *smb2req, |
536 | | uint16_t in_flags, |
537 | | const char *in_path) |
538 | 0 | { |
539 | 0 | struct tevent_req *req; |
540 | 0 | struct smbd_smb2_tree_connect_state *state; |
541 | 0 | NTSTATUS status; |
542 | |
|
543 | 0 | req = tevent_req_create(mem_ctx, &state, |
544 | 0 | struct smbd_smb2_tree_connect_state); |
545 | 0 | if (req == NULL) { |
546 | 0 | return NULL; |
547 | 0 | } |
548 | 0 | state->in_path = in_path; |
549 | |
|
550 | 0 | status = smbd_smb2_tree_connect(smb2req, |
551 | 0 | state->in_path, |
552 | 0 | &state->out_share_type, |
553 | 0 | &state->out_share_flags, |
554 | 0 | &state->out_capabilities, |
555 | 0 | &state->out_maximal_access, |
556 | 0 | &state->out_tree_id, |
557 | 0 | &state->disconnect); |
558 | 0 | if (tevent_req_nterror(req, status)) { |
559 | 0 | return tevent_req_post(req, ev); |
560 | 0 | } |
561 | | |
562 | 0 | tevent_req_done(req); |
563 | 0 | return tevent_req_post(req, ev); |
564 | 0 | } |
565 | | |
566 | | static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req, |
567 | | uint8_t *out_share_type, |
568 | | uint32_t *out_share_flags, |
569 | | uint32_t *out_capabilities, |
570 | | uint32_t *out_maximal_access, |
571 | | uint32_t *out_tree_id, |
572 | | bool *disconnect) |
573 | 0 | { |
574 | 0 | struct smbd_smb2_tree_connect_state *state = |
575 | 0 | tevent_req_data(req, |
576 | 0 | struct smbd_smb2_tree_connect_state); |
577 | 0 | NTSTATUS status; |
578 | |
|
579 | 0 | if (tevent_req_is_nterror(req, &status)) { |
580 | 0 | tevent_req_received(req); |
581 | 0 | return status; |
582 | 0 | } |
583 | | |
584 | 0 | *out_share_type = state->out_share_type; |
585 | 0 | *out_share_flags = state->out_share_flags; |
586 | 0 | *out_capabilities = state->out_capabilities; |
587 | 0 | *out_maximal_access = state->out_maximal_access; |
588 | 0 | *out_tree_id = state->out_tree_id; |
589 | 0 | *disconnect = state->disconnect; |
590 | |
|
591 | 0 | tevent_req_received(req); |
592 | 0 | return NT_STATUS_OK; |
593 | 0 | } |
594 | | |
595 | | static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, |
596 | | struct tevent_context *ev, |
597 | | struct smbd_smb2_request *smb2req); |
598 | | static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req); |
599 | | static void smbd_smb2_request_tdis_done(struct tevent_req *subreq); |
600 | | |
601 | | NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req) |
602 | 0 | { |
603 | 0 | NTSTATUS status; |
604 | 0 | struct tevent_req *subreq = NULL; |
605 | |
|
606 | 0 | status = smbd_smb2_request_verify_sizes(req, 0x04); |
607 | 0 | if (!NT_STATUS_IS_OK(status)) { |
608 | 0 | return smbd_smb2_request_error(req, status); |
609 | 0 | } |
610 | | |
611 | 0 | subreq = smbd_smb2_tdis_send(req, req->sconn->ev_ctx, req); |
612 | 0 | if (subreq == NULL) { |
613 | 0 | return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); |
614 | 0 | } |
615 | 0 | tevent_req_set_callback(subreq, smbd_smb2_request_tdis_done, req); |
616 | | |
617 | | /* |
618 | | * Avoid sending a STATUS_PENDING message, it's very likely |
619 | | * the client won't expect that. |
620 | | */ |
621 | 0 | return smbd_smb2_request_pending_queue(req, subreq, 0); |
622 | 0 | } |
623 | | |
624 | | static void smbd_smb2_request_tdis_done(struct tevent_req *subreq) |
625 | 0 | { |
626 | 0 | struct smbd_smb2_request *smb2req = |
627 | 0 | tevent_req_callback_data(subreq, |
628 | 0 | struct smbd_smb2_request); |
629 | 0 | DATA_BLOB outbody; |
630 | 0 | NTSTATUS status; |
631 | 0 | NTSTATUS error; |
632 | |
|
633 | 0 | status = smbd_smb2_tdis_recv(subreq); |
634 | 0 | TALLOC_FREE(subreq); |
635 | 0 | if (!NT_STATUS_IS_OK(status)) { |
636 | 0 | error = smbd_smb2_request_error(smb2req, status); |
637 | 0 | if (!NT_STATUS_IS_OK(error)) { |
638 | 0 | smbd_server_connection_terminate(smb2req->xconn, |
639 | 0 | nt_errstr(error)); |
640 | 0 | return; |
641 | 0 | } |
642 | 0 | return; |
643 | 0 | } |
644 | | |
645 | 0 | outbody = smbd_smb2_generate_outbody(smb2req, 0x04); |
646 | 0 | if (outbody.data == NULL) { |
647 | 0 | error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); |
648 | 0 | if (!NT_STATUS_IS_OK(error)) { |
649 | 0 | smbd_server_connection_terminate(smb2req->xconn, |
650 | 0 | nt_errstr(error)); |
651 | 0 | return; |
652 | 0 | } |
653 | 0 | return; |
654 | 0 | } |
655 | | |
656 | 0 | SSVAL(outbody.data, 0x00, 0x04); /* struct size */ |
657 | 0 | SSVAL(outbody.data, 0x02, 0); /* reserved */ |
658 | |
|
659 | 0 | error = smbd_smb2_request_done(smb2req, outbody, NULL); |
660 | 0 | if (!NT_STATUS_IS_OK(error)) { |
661 | 0 | smbd_server_connection_terminate(smb2req->xconn, |
662 | 0 | nt_errstr(error)); |
663 | 0 | return; |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | | struct smbd_smb2_tdis_state { |
668 | | struct smbd_smb2_request *smb2req; |
669 | | struct tevent_queue *wait_queue; |
670 | | }; |
671 | | |
672 | | static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq); |
673 | | |
674 | | struct check_for_lease_break_fsp_cmp_state { |
675 | | struct smbXsrv_tcon *tcon; |
676 | | }; |
677 | | |
678 | | static bool check_for_lease_break_fsp_cmp_fn(struct files_struct *fsp, |
679 | | void *private_data) |
680 | 0 | { |
681 | 0 | struct check_for_lease_break_fsp_cmp_state *state = |
682 | 0 | (struct check_for_lease_break_fsp_cmp_state *)private_data; |
683 | |
|
684 | 0 | return (fsp->conn == state->tcon->compat); |
685 | 0 | } |
686 | | |
687 | | static struct tevent_req *smbd_smb2_tdis_send(TALLOC_CTX *mem_ctx, |
688 | | struct tevent_context *ev, |
689 | | struct smbd_smb2_request *smb2req) |
690 | 0 | { |
691 | 0 | struct tevent_req *req; |
692 | 0 | struct smbd_smb2_tdis_state *state; |
693 | 0 | struct tevent_req *subreq; |
694 | 0 | struct smbXsrv_connection *xconn = NULL; |
695 | 0 | struct check_for_lease_break_fsp_cmp_state fsp_cmp_state; |
696 | |
|
697 | 0 | req = tevent_req_create(mem_ctx, &state, |
698 | 0 | struct smbd_smb2_tdis_state); |
699 | 0 | if (req == NULL) { |
700 | 0 | return NULL; |
701 | 0 | } |
702 | 0 | state->smb2req = smb2req; |
703 | |
|
704 | 0 | state->wait_queue = tevent_queue_create(state, "tdis_wait_queue"); |
705 | 0 | if (tevent_req_nomem(state->wait_queue, req)) { |
706 | 0 | return tevent_req_post(req, ev); |
707 | 0 | } |
708 | | |
709 | | /* |
710 | | * Make sure that no new request will be able to use this tcon. |
711 | | */ |
712 | 0 | smb2req->tcon->status = NT_STATUS_NETWORK_NAME_DELETED; |
713 | |
|
714 | 0 | xconn = smb2req->xconn->client->connections; |
715 | 0 | for (; xconn != NULL; xconn = xconn->next) { |
716 | 0 | struct smbd_smb2_request *preq; |
717 | |
|
718 | 0 | for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { |
719 | 0 | if (preq == smb2req) { |
720 | | /* Can't cancel current request. */ |
721 | 0 | continue; |
722 | 0 | } |
723 | 0 | if (preq->tcon != smb2req->tcon) { |
724 | | /* Request on different tcon. */ |
725 | 0 | continue; |
726 | 0 | } |
727 | | |
728 | 0 | if (preq->subreq != NULL) { |
729 | 0 | tevent_req_cancel(preq->subreq); |
730 | 0 | } |
731 | | |
732 | | /* |
733 | | * Now wait until the request is finished. |
734 | | * |
735 | | * We don't set a callback, as we just want to block the |
736 | | * wait queue and the talloc_free() of the request will |
737 | | * remove the item from the wait queue. |
738 | | */ |
739 | 0 | subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); |
740 | 0 | if (tevent_req_nomem(subreq, req)) { |
741 | 0 | return tevent_req_post(req, ev); |
742 | 0 | } |
743 | 0 | } |
744 | 0 | } |
745 | | |
746 | 0 | fsp_cmp_state = (struct check_for_lease_break_fsp_cmp_state) { |
747 | 0 | .tcon = smb2req->tcon, |
748 | 0 | }; |
749 | |
|
750 | 0 | smbXsrv_wait_for_handle_lease_break(req, |
751 | 0 | ev, |
752 | 0 | smb2req->xconn->client, |
753 | 0 | state->wait_queue, |
754 | 0 | check_for_lease_break_fsp_cmp_fn, |
755 | 0 | &fsp_cmp_state); |
756 | 0 | if (!tevent_req_is_in_progress(req)) { |
757 | 0 | return tevent_req_post(req, ev); |
758 | 0 | } |
759 | | |
760 | | /* |
761 | | * Now we add our own waiter to the end of the queue, |
762 | | * this way we get notified when all pending requests are finished |
763 | | * and send to the socket. |
764 | | */ |
765 | 0 | subreq = tevent_queue_wait_send(state, ev, state->wait_queue); |
766 | 0 | if (tevent_req_nomem(subreq, req)) { |
767 | 0 | return tevent_req_post(req, ev); |
768 | 0 | } |
769 | 0 | tevent_req_set_callback(subreq, smbd_smb2_tdis_wait_done, req); |
770 | |
|
771 | 0 | return req; |
772 | 0 | } |
773 | | |
774 | | static void smbd_smb2_tdis_wait_done(struct tevent_req *subreq) |
775 | 0 | { |
776 | 0 | struct tevent_req *req = tevent_req_callback_data( |
777 | 0 | subreq, struct tevent_req); |
778 | 0 | struct smbd_smb2_tdis_state *state = tevent_req_data( |
779 | 0 | req, struct smbd_smb2_tdis_state); |
780 | 0 | NTSTATUS status; |
781 | |
|
782 | 0 | tevent_queue_wait_recv(subreq); |
783 | 0 | TALLOC_FREE(subreq); |
784 | | |
785 | | /* |
786 | | * As we've been awoken, we may have changed |
787 | | * uid in the meantime. Ensure we're still |
788 | | * root (SMB2_OP_TDIS has .as_root = true). |
789 | | */ |
790 | 0 | change_to_root_user(); |
791 | |
|
792 | 0 | status = smbXsrv_tcon_disconnect(state->smb2req->tcon, |
793 | 0 | state->smb2req->tcon->compat->vuid); |
794 | 0 | if (tevent_req_nterror(req, status)) { |
795 | 0 | return; |
796 | 0 | } |
797 | | |
798 | | /* We did tear down the tcon. */ |
799 | 0 | TALLOC_FREE(state->smb2req->tcon); |
800 | 0 | tevent_req_done(req); |
801 | 0 | } |
802 | | |
803 | | static NTSTATUS smbd_smb2_tdis_recv(struct tevent_req *req) |
804 | 0 | { |
805 | 0 | return tevent_req_simple_recv_ntstatus(req); |
806 | 0 | } |