s3:libsmb:smb2: pass previous session ID to session setup, not previous session
[samba.git] / source3 / libsmb / smb2cli_session.c
1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Volker Lendecke 2011
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "includes.h"
21 #include "client.h"
22 #include "async_smb.h"
23 #include "smb2cli.h"
24 #include "../libcli/smb/smbXcli_base.h"
25 #include "libsmb/proto.h"
26 #include "lib/util/tevent_ntstatus.h"
27 #include "../libcli/auth/spnego.h"
28 #include "../auth/ntlmssp/ntlmssp.h"
29
30 struct smb2cli_session_setup_state {
31         struct smbXcli_session *session;
32         uint8_t fixed[24];
33         uint8_t dyn_pad[1];
34         struct iovec *recv_iov;
35         DATA_BLOB out_security_buffer;
36         NTSTATUS status;
37 };
38
39 static void smb2cli_session_setup_done(struct tevent_req *subreq);
40
41 struct tevent_req *smb2cli_session_setup_send(TALLOC_CTX *mem_ctx,
42                                 struct tevent_context *ev,
43                                 struct smbXcli_conn *conn,
44                                 uint32_t timeout_msec,
45                                 struct smbXcli_session *session,
46                                 uint8_t in_flags,
47                                 uint32_t in_capabilities,
48                                 uint32_t in_channel,
49                                 uint64_t in_previous_session_id,
50                                 const DATA_BLOB *in_security_buffer)
51 {
52         struct tevent_req *req, *subreq;
53         struct smb2cli_session_setup_state *state;
54         uint8_t *buf;
55         uint8_t *dyn;
56         size_t dyn_len;
57         uint8_t security_mode;
58         uint16_t security_buffer_offset = 0;
59         uint16_t security_buffer_length = 0;
60
61         req = tevent_req_create(mem_ctx, &state,
62                                 struct smb2cli_session_setup_state);
63         if (req == NULL) {
64                 return NULL;
65         }
66
67         if (session == NULL) {
68                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
69                 return tevent_req_post(req, ev);
70         }
71         state->session = session;
72         security_mode = smb2cli_session_security_mode(session);
73
74         if (in_security_buffer) {
75                 if (in_security_buffer->length > UINT16_MAX) {
76                         tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX);
77                         return tevent_req_post(req, ev);
78                 }
79                 security_buffer_offset = SMB2_HDR_BODY + 24;
80                 security_buffer_length = in_security_buffer->length;
81         }
82
83         buf = state->fixed;
84
85         SSVAL(buf,  0, 25);
86         SCVAL(buf,  2, in_flags);
87         SCVAL(buf,  3, security_mode);
88         SIVAL(buf,  4, in_capabilities);
89         SIVAL(buf,  8, in_channel);
90         SSVAL(buf, 12, security_buffer_offset);
91         SSVAL(buf, 14, security_buffer_length);
92         SBVAL(buf, 16, in_previous_session_id);
93
94         if (security_buffer_length > 0) {
95                 dyn = in_security_buffer->data;
96                 dyn_len = in_security_buffer->length;
97         } else {
98                 dyn = state->dyn_pad;;
99                 dyn_len = sizeof(state->dyn_pad);
100         }
101
102         subreq = smb2cli_req_send(state, ev,
103                                   conn, SMB2_OP_SESSSETUP,
104                                   0, 0, /* flags */
105                                   timeout_msec,
106                                   0xFEFF,
107                                   0, /* tid */
108                                   session,
109                                   state->fixed, sizeof(state->fixed),
110                                   dyn, dyn_len);
111         if (tevent_req_nomem(subreq, req)) {
112                 return tevent_req_post(req, ev);
113         }
114         tevent_req_set_callback(subreq, smb2cli_session_setup_done, req);
115         return req;
116 }
117
118 static void smb2cli_session_setup_done(struct tevent_req *subreq)
119 {
120         struct tevent_req *req =
121                 tevent_req_callback_data(subreq,
122                 struct tevent_req);
123         struct smb2cli_session_setup_state *state =
124                 tevent_req_data(req,
125                 struct smb2cli_session_setup_state);
126         NTSTATUS status;
127         uint64_t current_session_id;
128         uint64_t session_id;
129         uint16_t session_flags;
130         uint16_t expected_offset = 0;
131         uint16_t security_buffer_offset;
132         uint16_t security_buffer_length;
133         uint8_t *security_buffer_data = NULL;
134         const uint8_t *hdr;
135         const uint8_t *body;
136         static const struct smb2cli_req_expected_response expected[] = {
137         {
138                 .status = NT_STATUS_MORE_PROCESSING_REQUIRED,
139                 .body_size = 0x09
140         },
141         {
142                 .status = NT_STATUS_OK,
143                 .body_size = 0x09
144         }
145         };
146
147         status = smb2cli_req_recv(subreq, state, &state->recv_iov,
148                                   expected, ARRAY_SIZE(expected));
149         TALLOC_FREE(subreq);
150         if (!NT_STATUS_IS_OK(status) &&
151             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
152                 tevent_req_nterror(req, status);
153                 return;
154         }
155
156         hdr = (const uint8_t *)state->recv_iov[0].iov_base;
157         body = (const uint8_t *)state->recv_iov[1].iov_base;
158
159         session_id = BVAL(hdr, SMB2_HDR_SESSION_ID);
160         session_flags = SVAL(body, 2);
161
162         security_buffer_offset = SVAL(body, 4);
163         security_buffer_length = SVAL(body, 6);
164
165         if (security_buffer_length > 0) {
166                 expected_offset = SMB2_HDR_BODY + 8;
167         }
168         if (security_buffer_offset != 0) {
169                 security_buffer_data = (uint8_t *)state->recv_iov[2].iov_base;
170                 expected_offset = SMB2_HDR_BODY + 8;
171         }
172
173         if (security_buffer_offset != expected_offset) {
174                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
175                 return;
176         }
177         if (security_buffer_length > state->recv_iov[2].iov_len) {
178                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
179                 return;
180         }
181
182         state->out_security_buffer.data = security_buffer_data;
183         state->out_security_buffer.length = security_buffer_length;
184
185         current_session_id = smb2cli_session_current_id(state->session);
186         if (current_session_id == 0) {
187                 /* A new session was requested */
188                 current_session_id = session_id;
189         }
190
191         if (current_session_id != session_id) {
192                 tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE);
193                 return;
194         }
195
196         smb2cli_session_set_id_and_flags(state->session,
197                                          session_id, session_flags);
198
199         state->status = status;
200         tevent_req_done(req);
201 }
202
203 NTSTATUS smb2cli_session_setup_recv(struct tevent_req *req,
204                                     TALLOC_CTX *mem_ctx,
205                                     struct iovec **recv_iov,
206                                     DATA_BLOB *out_security_buffer)
207 {
208         struct smb2cli_session_setup_state *state =
209                 tevent_req_data(req,
210                 struct smb2cli_session_setup_state);
211         NTSTATUS status;
212         struct iovec *_tmp;
213
214         if (tevent_req_is_nterror(req, &status)) {
215                 tevent_req_received(req);
216                 return status;
217         }
218
219         if (recv_iov == NULL) {
220                 recv_iov = &_tmp;
221         }
222
223         *recv_iov = talloc_move(mem_ctx, &state->recv_iov);
224
225         *out_security_buffer = state->out_security_buffer;
226
227         /*
228          * Return the status from the server:
229          * NT_STATUS_MORE_PROCESSING_REQUIRED or
230          * NT_STATUS_OK.
231          */
232         status = state->status;
233         tevent_req_received(req);
234         return status;
235 }
236
237 struct smb2cli_logoff_state {
238         struct cli_state *cli;
239         uint8_t fixed[4];
240 };
241
242 static void smb2cli_logoff_done(struct tevent_req *subreq);
243
244 struct tevent_req *smb2cli_logoff_send(TALLOC_CTX *mem_ctx,
245                                        struct tevent_context *ev,
246                                        struct cli_state *cli)
247 {
248         struct tevent_req *req, *subreq;
249         struct smb2cli_logoff_state *state;
250
251         req = tevent_req_create(mem_ctx, &state,
252                                 struct smb2cli_logoff_state);
253         if (req == NULL) {
254                 return NULL;
255         }
256         state->cli = cli;
257         SSVAL(state->fixed, 0, 4);
258
259         subreq = smb2cli_req_send(state, ev,
260                                   cli->conn, SMB2_OP_LOGOFF,
261                                   0, 0, /* flags */
262                                   cli->timeout,
263                                   cli->smb2.pid,
264                                   0, /* tid */
265                                   cli->smb2.session,
266                                   state->fixed, sizeof(state->fixed),
267                                   NULL, 0);
268         if (tevent_req_nomem(subreq, req)) {
269                 return tevent_req_post(req, ev);
270         }
271         tevent_req_set_callback(subreq, smb2cli_logoff_done, req);
272         return req;
273 }
274
275 static void smb2cli_logoff_done(struct tevent_req *subreq)
276 {
277         struct tevent_req *req =
278                 tevent_req_callback_data(subreq,
279                 struct tevent_req);
280         struct smb2cli_logoff_state *state =
281                 tevent_req_data(req,
282                 struct smb2cli_logoff_state);
283         NTSTATUS status;
284         struct iovec *iov;
285         static const struct smb2cli_req_expected_response expected[] = {
286         {
287                 .status = NT_STATUS_OK,
288                 .body_size = 0x04
289         }
290         };
291
292         status = smb2cli_req_recv(subreq, state, &iov,
293                                   expected, ARRAY_SIZE(expected));
294         TALLOC_FREE(subreq);
295         TALLOC_FREE(state->cli->smb2.session);
296         if (tevent_req_nterror(req, status)) {
297                 return;
298         }
299         tevent_req_done(req);
300 }
301
302 NTSTATUS smb2cli_logoff_recv(struct tevent_req *req)
303 {
304         return tevent_req_simple_recv_ntstatus(req);
305 }
306
307 NTSTATUS smb2cli_logoff(struct cli_state *cli)
308 {
309         TALLOC_CTX *frame = talloc_stackframe();
310         struct event_context *ev;
311         struct tevent_req *req;
312         NTSTATUS status = NT_STATUS_NO_MEMORY;
313
314         if (cli_has_async_calls(cli)) {
315                 /*
316                  * Can't use sync call while an async call is in flight
317                  */
318                 status = NT_STATUS_INVALID_PARAMETER;
319                 goto fail;
320         }
321         ev = event_context_init(frame);
322         if (ev == NULL) {
323                 goto fail;
324         }
325         req = smb2cli_logoff_send(frame, ev, cli);
326         if (req == NULL) {
327                 goto fail;
328         }
329         if (!tevent_req_poll_ntstatus(req, ev, &status)) {
330                 goto fail;
331         }
332         status = smb2cli_logoff_recv(req);
333  fail:
334         TALLOC_FREE(frame);
335         return status;
336 }