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