s3:smbd: create a connection_struct in SMB2 Tree Connect
[kai/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/globals.h"
23 #include "../source4/libcli/smb2/smb2_constants.h"
24
25 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
26                                        const char *share,
27                                        uint32_t *out_tree_id);
28
29 NTSTATUS smbd_smb2_request_process_tcon(struct smbd_smb2_request *req)
30 {
31         const uint8_t *inbody;
32         int i = req->current_idx;
33         uint8_t *outhdr;
34         DATA_BLOB outbody;
35         size_t expected_body_size = 0x09;
36         size_t body_size;
37         uint16_t in_path_offset;
38         uint16_t in_path_length;
39         DATA_BLOB in_path_buffer;
40         char *in_path_string;
41         size_t in_path_string_size;
42         uint32_t out_tree_id;
43         NTSTATUS status;
44         bool ok;
45
46         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
47                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
48         }
49
50         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
51
52         body_size = SVAL(inbody, 0x00);
53         if (body_size != expected_body_size) {
54                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
55         }
56
57         in_path_offset = SVAL(inbody, 0x04);
58         in_path_length = SVAL(inbody, 0x06);
59
60         if (in_path_offset != (SMB2_HDR_BODY + (body_size & 0xFFFFFFFE))) {
61                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
62         }
63
64         if (in_path_length > req->in.vector[i+2].iov_len) {
65                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
66         }
67
68         in_path_buffer.data = (uint8_t *)req->in.vector[i+2].iov_base;
69         in_path_buffer.length = in_path_length;
70
71         ok = convert_string_talloc(req, CH_UTF16, CH_UNIX,
72                                    in_path_buffer.data,
73                                    in_path_buffer.length,
74                                    &in_path_string,
75                                    &in_path_string_size, false);
76         if (!ok) {
77                 return smbd_smb2_request_error(req, NT_STATUS_ILLEGAL_CHARACTER);
78         }
79
80         status = smbd_smb2_tree_connect(req, in_path_string, &out_tree_id);
81         if (!NT_STATUS_IS_OK(status)) {
82                 return smbd_smb2_request_error(req, status);
83         }
84
85         outhdr = (uint8_t *)req->out.vector[i].iov_base;
86
87         outbody = data_blob_talloc(req->out.vector, NULL, 0x10);
88         if (outbody.data == NULL) {
89                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
90         }
91
92         SIVAL(outhdr, SMB2_HDR_TID, out_tree_id);
93
94         SSVAL(outbody.data, 0x00, 0x10);        /* struct size */
95         SCVAL(outbody.data, 0x02, 0);           /* share type */
96         SCVAL(outbody.data, 0x03, 0);           /* reserved */
97         SIVAL(outbody.data, 0x04, 0);           /* share flags */
98         SIVAL(outbody.data, 0x08, 0);           /* capabilities */
99         SIVAL(outbody.data, 0x0C, 0);           /* maximal access */
100
101         return smbd_smb2_request_done(req, outbody, NULL);
102 }
103
104 static int smbd_smb2_tcon_destructor(struct smbd_smb2_tcon *tcon)
105 {
106         if (tcon->session == NULL) {
107                 return 0;
108         }
109
110         idr_remove(tcon->session->tcons.idtree, tcon->tid);
111         DLIST_REMOVE(tcon->session->tcons.list, tcon);
112
113         conn_free(tcon->session->conn, tcon->compat_conn);
114
115         tcon->compat_conn = NULL;
116         tcon->tid = 0;
117         tcon->session = NULL;
118
119         return 0;
120 }
121
122 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
123                                        const char *in_path,
124                                        uint32_t *out_tree_id)
125 {
126         const char *share = in_path;
127         fstring service;
128         int snum = -1;
129         struct smbd_smb2_tcon *tcon;
130         int id;
131         NTSTATUS status;
132
133         if (strncmp(share, "\\\\", 2) == 0) {
134                 const char *p = strchr(share+2, '\\');
135                 if (p) {
136                         share = p + 1;
137                 }
138         }
139
140         DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
141                   in_path, share));
142
143         fstrcpy(service, share);
144
145         strlower_m(service);
146
147         snum = find_service(service);
148         if (snum < 0) {
149                 DEBUG(1,("smbd_smb2_tree_connect: couldn't find service %s\n",
150                          service));
151                 return NT_STATUS_BAD_NETWORK_NAME;
152         }
153
154         /* TODO: do more things... */
155
156         /* create a new tcon as child of the session */
157         tcon = talloc_zero(req->session, struct smbd_smb2_tcon);
158         if (tcon == NULL) {
159                 return NT_STATUS_NO_MEMORY;
160         }
161         id = idr_get_new_random(req->session->tcons.idtree,
162                                 tcon,
163                                 req->session->tcons.limit);
164         if (id == -1) {
165                 TALLOC_FREE(tcon);
166                 return NT_STATUS_INSUFFICIENT_RESOURCES;
167         }
168         tcon->tid = id;
169         tcon->snum = snum;
170
171         DLIST_ADD_END(req->session->tcons.list, tcon,
172                       struct smbd_smb2_tcon *);
173         tcon->session = req->session;
174         talloc_set_destructor(tcon, smbd_smb2_tcon_destructor);
175
176         tcon->compat_conn = make_connection_snum(req->conn,
177                                         snum, req->session->compat_vuser,
178                                         data_blob_null, "???",
179                                         &status);
180         if (tcon->compat_conn == NULL) {
181                 TALLOC_FREE(tcon);
182                 return status;
183         }
184         tcon->compat_conn->cnum = tcon->tid;
185
186         *out_tree_id = tcon->tid;
187         return NT_STATUS_OK;
188 }
189
190 NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
191 {
192         const uint8_t *inhdr;
193         int i = req->current_idx;
194         uint32_t in_tid;
195         void *p;
196         struct smbd_smb2_tcon *tcon;
197
198         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
199
200         in_tid = IVAL(inhdr, SMB2_HDR_TID);
201
202         /* lookup an existing session */
203         p = idr_find(req->session->tcons.idtree, in_tid);
204         if (p == NULL) {
205                 return NT_STATUS_NETWORK_NAME_DELETED;
206         }
207         tcon = talloc_get_type_abort(p, struct smbd_smb2_tcon);
208
209         if (!change_to_user(tcon->compat_conn,req->session->vuid)) {
210                 return NT_STATUS_ACCESS_DENIED;
211         }
212
213         req->tcon = tcon;
214         return NT_STATUS_OK;
215 }
216
217 NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req)
218 {
219         const uint8_t *inbody;
220         int i = req->current_idx;
221         DATA_BLOB outbody;
222         size_t expected_body_size = 0x04;
223         size_t body_size;
224
225         if (req->in.vector[i+1].iov_len != (expected_body_size & 0xFFFFFFFE)) {
226                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
227         }
228
229         inbody = (const uint8_t *)req->in.vector[i+1].iov_base;
230
231         body_size = SVAL(inbody, 0x00);
232         if (body_size != expected_body_size) {
233                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
234         }
235
236         /*
237          * TODO: cancel all outstanding requests on the tcon
238          *       and delete all file handles.
239          */
240         TALLOC_FREE(req->tcon);
241
242         outbody = data_blob_talloc(req->out.vector, NULL, 0x04);
243         if (outbody.data == NULL) {
244                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
245         }
246
247         SSVAL(outbody.data, 0x00, 0x04);        /* struct size */
248         SSVAL(outbody.data, 0x02, 0);           /* reserved */
249
250         return smbd_smb2_request_done(req, outbody, NULL);
251 }