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