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