s3:smbd: implement SMB2 Tree Connect
[ira/wip.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         tcon->tid = 0;
114         tcon->session = NULL;
115
116         return 0;
117 }
118
119 static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req,
120                                        const char *in_path,
121                                        uint32_t *out_tree_id)
122 {
123         const char *share = in_path;
124         fstring service;
125         int snum = -1;
126         struct smbd_smb2_tcon *tcon;
127         int id;
128
129         if (strncmp(share, "\\\\", 2) == 0) {
130                 const char *p = strchr(share+2, '\\');
131                 if (p) {
132                         share = p + 1;
133                 }
134         }
135
136         DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n",
137                   in_path, share));
138
139         fstrcpy(service, share);
140
141         strlower_m(service);
142
143         snum = find_service(service);
144         if (snum < 0) {
145                 DEBUG(1,("smbd_smb2_tree_connect: couldn't find service %s\n",
146                          service));
147                 return NT_STATUS_BAD_NETWORK_NAME;
148         }
149
150         /* TODO: do more things... */
151
152         /* create a new tcon as child of the session */
153         tcon = talloc_zero(req->session, struct smbd_smb2_tcon);
154         if (tcon == NULL) {
155                 return NT_STATUS_NO_MEMORY;
156         }
157         id = idr_get_new_random(req->session->tcons.idtree,
158                                 tcon,
159                                 req->session->tcons.limit);
160         if (id == -1) {
161                 return NT_STATUS_INSUFFICIENT_RESOURCES;
162         }
163         tcon->tid = id;
164         tcon->snum = snum;
165
166         DLIST_ADD_END(req->session->tcons.list, tcon,
167                       struct smbd_smb2_tcon *);
168         tcon->session = req->session;
169         talloc_set_destructor(tcon, smbd_smb2_tcon_destructor);
170
171         *out_tree_id = tcon->tid;
172         return NT_STATUS_OK;
173 }
174
175 NTSTATUS smbd_smb2_request_check_tcon(struct smbd_smb2_request *req)
176 {
177         const uint8_t *inhdr;
178         int i = req->current_idx;
179         uint32_t in_tid;
180         void *p;
181         struct smbd_smb2_tcon *tcon;
182
183         inhdr = (const uint8_t *)req->in.vector[i+0].iov_base;
184
185         in_tid = IVAL(inhdr, SMB2_HDR_TID);
186
187         /* lookup an existing session */
188         p = idr_find(req->session->tcons.idtree, in_tid);
189         if (p == NULL) {
190                 return NT_STATUS_NETWORK_NAME_DELETED;
191         }
192         tcon = talloc_get_type_abort(p, struct smbd_smb2_tcon);
193
194         req->tcon = tcon;
195         return NT_STATUS_OK;
196 }