bdd18d5d07ea0dff6c087bed38c8c4b121cddeda
[samba.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
28 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
29                                        const char *in_path,
30                                        uint8_t *out_share_type,
31                                        uint32_t *out_share_flags,
32                                        uint32_t *out_capabilities,
33                                        uint32_t *out_maximal_access,
34                                        uint32_t *out_tree_id);
35
36 NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
37 {
38         const uint8_t *inbody;
39         int i = req->current_idx;
40         uint8_t *outhdr;
41         DATA_BLOB outbody;
42         size_t expected_body_size = 0x09;
43         size_t body_size;
44         uint16_t in_path_offset;
45         uint16_t in_path_length;
46         DATA_BLOB in_path_buffer;
47         char *in_path_string;
48         size_t in_path_string_size;
49         uint8_t out_share_type = 0;
50         uint32_t out_share_flags = 0;
51         uint32_t out_capabilities = 0;
52         uint32_t out_maximal_access = 0;
53         uint32_t out_tree_id = 0;
54         NTSTATUS status;
55         bool ok;
56
57         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
58                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
59         }
60
61         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
62
63         body_size = SVAL(inbody, 0x00);
64         if (body_size != expected_body_size) {
65                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
66         }
67
68         in_path_offset = SVAL(inbody, 0x04);
69         in_path_length = SVAL(inbody, 0x06);
70
71         if (in_path_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
72                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
73         }
74
75         if (in_path_length > req->in.vector[i+2].iov_len) {
76                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
77         }
78
79         in_path_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
80         in_path_buffer.length = in_path_length;
81
82         ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
83                                    in_path_buffer.data,
84                                    in_path_buffer.length,
85                                    &in_path_string,
86                                    &in_path_string_size);
87         if (!ok) {
88                 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
89         }
90
91         status = smbd_smb2_tree_connect(req, in_path_string,
92                                         &out_share_type,
93                                         &out_share_flags,
94                                         &out_capabilities,
95                                         &out_maximal_access,
96                                         &out_tree_id);
97         if (!NT_STATUS_IS_OK(status)) {
98                 return smbd_smb2_request_error(req, status);
99         }
100
101         outhdr = (uint8_t *)req->out.vector[i].iov_base;
102
103         outbody = data_blob_talloc(req->out.vector, NULL, 0x10);
104         if (outbody.data == NULL) {
105                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
106         }
107
108         SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
109
110         SSVAL(outbody.data, 0x00, 0x10);        /* struct size */
111         SCVAL(outbody.data, 0x02,
112               out_share_type);                  /* share type */
113         SCVAL(outbody.data, 0x03, 0);           /* reserved */
114         SIVAL(outbody.data, 0x04,
115               out_share_flags);                 /* share flags */
116         SIVAL(outbody.data, 0x08,
117               out_capabilities);                /* capabilities */
118         SIVAL(outbody.data, 0x0C,
119               out_maximal_access);              /* maximal access */
120
121         return smbd_smb2_request_done(req, outbody, NULL);
122 }
123
124 static int smbd_smb2_tcon_destructor(struct smbd_smb2_tcon *tcon)
125 {
126         if (tcon->session == NULL) {
127                 return 0;
128         }
129
130         idr_remove(tcon->session->tcons.idtree, tcon->tid);
131         DLIST_REMOVE(tcon->session->tcons.list, tcon);
132         SMB_ASSERT(tcon->session->sconn->num_tcons_open > 0);
133         tcon->session->sconn->num_tcons_open--;
134
135         if (tcon->compat_conn) {
136                 set_current_service(tcon->compat_conn, 0, true);
137                 close_cnum(tcon->compat_conn, tcon->session->vuid);
138         }
139
140         tcon->compat_conn = NULL;
141         tcon->tid = 0;
142         tcon->session = NULL;
143
144         return 0;
145 }
146
147 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
148                                        const char *in_path,
149                                        uint8_t *out_share_type,
150                                        uint32_t *out_share_flags,
151                                        uint32_t *out_capabilities,
152                                        uint32_t *out_maximal_access,
153                                        uint32_t *out_tree_id)
154 {
155         const char *share = in_path;
156         char *service = NULL;
157         int snum = -1;
158         struct smbd_smb2_tcon *tcon;
159         connection_struct *compat_conn = NULL;
160         user_struct *compat_vuser = req->session->compat_vuser;
161         int id;
162         NTSTATUS status;
163
164         if (strncmp(share, "\\\\", 2) == 0) {
165                 const char *p = strchr(share+2, '\\');
166                 if (p) {
167                         share = p + 1;
168                 }
169         }
170
171         DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
172                   in_path, share));
173
174         service = talloc_strdup(talloc_tos(), share);
175         if(!service) {
176                 return NT_STATUS_NO_MEMORY;
177         }
178
179         strlower_m(service);
180
181         /* TODO: do more things... */
182         if (strequal(service,HOMES_NAME)) {
183                 if (compat_vuser->homes_snum == -1) {
184                         DEBUG(2, ("[homes] share not available for "
185                                 "user %s because it was not found "
186                                 "or created at session setup "
187                                 "time\n",
188                                 compat_vuser->session_info->unix_name));
189                         return NT_STATUS_BAD_NETWORK_NAME;
190                 }
191                 snum = compat_vuser->homes_snum;
192         } else if ((compat_vuser->homes_snum != -1)
193                    && strequal(service,
194                         lp_servicename(compat_vuser->homes_snum))) {
195                 snum = compat_vuser->homes_snum;
196         } else {
197                 snum = find_service(talloc_tos(), service, &service);
198                 if (!service) {
199                         return NT_STATUS_NO_MEMORY;
200                 }
201         }
202
203         if (snum < 0) {
204                 DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n",
205                          service));
206                 return NT_STATUS_BAD_NETWORK_NAME;
207         }
208
209         /* create a new tcon as child of the session */
210         tcon = talloc_zero(req->session, struct smbd_smb2_tcon);
211         if (tcon == NULL) {
212                 return NT_STATUS_NO_MEMORY;
213         }
214         id = idr_get_new_random(req->session->tcons.idtree,
215                                 tcon,
216                                 req->session->tcons.limit);
217         if (id == -1) {
218                 TALLOC_FREE(tcon);
219                 return NT_STATUS_INSUFFICIENT_RESOURCES;
220         }
221         tcon->tid = id;
222         tcon->snum = snum;
223
224         DLIST_ADD_END(req->session->tcons.list, tcon,
225                       struct smbd_smb2_tcon *);
226         tcon->session = req->session;
227         tcon->session->sconn->num_tcons_open++;
228         talloc_set_destructor(tcon, smbd_smb2_tcon_destructor);
229
230         compat_conn = make_connection_snum(req->sconn,
231                                         snum, req->session->compat_vuser,
232                                         data_blob_null, "???",
233                                         &status);
234         if (compat_conn == NULL) {
235                 TALLOC_FREE(tcon);
236                 return status;
237         }
238         tcon->compat_conn = talloc_move(tcon, &compat_conn);
239         tcon->compat_conn->cnum = tcon->tid;
240
241         if (IS_PRINT(tcon->compat_conn)) {
242                 *out_share_type = SMB2_SHARE_TYPE_PRINT;
243         } else if (IS_IPC(tcon->compat_conn)) {
244                 *out_share_type = SMB2_SHARE_TYPE_PIPE;
245         } else {
246                 *out_share_type = SMB2_SHARE_TYPE_DISK;
247         }
248
249         *out_share_flags = SMB2_SHAREFLAG_ALLOW_NAMESPACE_CACHING;
250
251         if (lp_msdfs_root(SNUM(tcon->compat_conn)) && lp_host_msdfs()) {
252                 *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT);
253                 *out_capabilities = SMB2_SHARE_CAP_DFS;
254         } else {
255                 *out_capabilities = 0;
256         }
257
258         switch(lp_csc_policy(SNUM(tcon->compat_conn))) {
259         case CSC_POLICY_MANUAL:
260                 break;
261         case CSC_POLICY_DOCUMENTS:
262                 *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING;
263                 break;
264         case CSC_POLICY_PROGRAMS:
265                 *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING;
266                 break;
267         case CSC_POLICY_DISABLE:
268                 *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING;
269                 break;
270         default:
271                 break;
272         }
273
274         *out_maximal_access = FILE_GENERIC_ALL;
275
276         *out_tree_id = tcon->tid;
277         return NT_STATUS_OK;
278 }
279
280 NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
281 {
282         const uint8_t *inhdr;
283         const uint8_t *outhdr;
284         int i = req->current_idx;
285         uint32_t in_tid;
286         void *p;
287         struct smbd_smb2_tcon *tcon;
288         bool chained_fixup = false;
289
290         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
291
292         in_tid = IVAL(inhdr, SMB2_HDR_TID);
293
294         if (in_tid == (0xFFFFFFFF)) {
295                 if (req->async) {
296                         /*
297                          * async request - fill in tid from
298                          * already setup out.vector[].iov_base.
299                          */
300                         outhdr = (const uint8_t *)req->out.vector[i].iov_base;
301                         in_tid = IVAL(outhdr, SMB2_HDR_TID);
302                 } else if (i > 2) {
303                         /*
304                          * Chained request - fill in tid from
305                          * the previous request out.vector[].iov_base.
306                          */
307                         outhdr = (const uint8_t *)req->out.vector[i-3].iov_base;
308                         in_tid = IVAL(outhdr, SMB2_HDR_TID);
309                         chained_fixup = true;
310                 }
311         }
312
313         /* lookup an existing session */
314         p = idr_find(req->session->tcons.idtree, in_tid);
315         if (p == NULL) {
316                 return NT_STATUS_NETWORK_NAME_DELETED;
317         }
318         tcon = talloc_get_type_abort(p, struct smbd_smb2_tcon);
319
320         if (!change_to_user(tcon->compat_conn,req->session->vuid)) {
321                 return NT_STATUS_ACCESS_DENIED;
322         }
323
324         /* should we pass FLAG_CASELESS_PATHNAMES here? */
325         if (!set_current_service(tcon->compat_conn, 0, true)) {
326                 return NT_STATUS_ACCESS_DENIED;
327         }
328
329         req->tcon = tcon;
330
331         if (chained_fixup) {
332                 /* Fix up our own outhdr. */
333                 outhdr = (const uint8_t *)req->out.vector[i].iov_base;
334                 SIVAL(outhdr, SMB2_HDR_TID, in_tid);
335         }
336
337         return NT_STATUS_OK;
338 }
339
340 NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
341 {
342         const uint8_t *inbody;
343         int i = req->current_idx;
344         DATA_BLOB outbody;
345         size_t expected_body_size = 0x04;
346         size_t body_size;
347
348         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
349                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
350         }
351
352         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
353
354         body_size = SVAL(inbody, 0x00);
355         if (body_size != expected_body_size) {
356                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
357         }
358
359         /*
360          * TODO: cancel all outstanding requests on the tcon
361          *       and delete all file handles.
362          */
363         TALLOC_FREE(req->tcon);
364
365         outbody = data_blob_talloc(req->out.vector, NULL, 0x04);
366         if (outbody.data == NULL) {
367                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
368         }
369
370         SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
371         SSVAL(outbody.data, 0x02, 0);           /* reserved */
372
373         return smbd_smb2_request_done(req, outbody, NULL);
374 }