s3:libsmb: make use of cli_state_disconnect()
[samba.git] / source3 / libsmb / smb2cli_base.c
1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Volker Lendecke 2011
5    Copyright (C) Stefan Metzmacher 2011
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 "client.h"
23 #include "read_smb.h"
24 #include "smb2cli_base.h"
25 #include "lib/async_req/async_sock.h"
26 #include "lib/util/tevent_ntstatus.h"
27
28 struct smb2cli_req_state {
29         struct tevent_context *ev;
30         struct cli_state *cli;
31
32         const uint8_t *fixed;
33         uint16_t fixed_len;
34         const uint8_t *dyn;
35         uint16_t dyn_len;
36
37         uint8_t nbt[4];
38         uint8_t hdr[64];
39         uint8_t pad[7]; /* padding space for compounding */
40
41         /* always an array of 3 talloc elements */
42         struct iovec *recv_iov;
43 };
44
45 static void smb2cli_req_unset_pending(struct tevent_req *req)
46 {
47         struct smb2cli_req_state *state =
48                 tevent_req_data(req,
49                 struct smb2cli_req_state);
50         struct cli_state *cli = state->cli;
51         int num_pending = talloc_array_length(cli->conn.pending);
52         int i;
53
54         talloc_set_destructor(req, NULL);
55
56         if (num_pending == 1) {
57                 /*
58                  * The pending read_smb tevent_req is a child of
59                  * cli->conn.pending. So if nothing is pending anymore,
60                  * we need to delete the socket read fde.
61                  */
62                 TALLOC_FREE(cli->conn.pending);
63                 return;
64         }
65
66         for (i=0; i<num_pending; i++) {
67                 if (req == cli->conn.pending[i]) {
68                         break;
69                 }
70         }
71         if (i == num_pending) {
72                 /*
73                  * Something's seriously broken. Just returning here is the
74                  * right thing nevertheless, the point of this routine is to
75                  * remove ourselves from cli->conn.pending.
76                  */
77                 return;
78         }
79
80         /*
81          * Remove ourselves from the cli->pending array
82          */
83         for (; i < (num_pending - 1); i++) {
84                 cli->conn.pending[i] = cli->conn.pending[i+1];
85         }
86
87         /*
88          * No NULL check here, we're shrinking by sizeof(void *), and
89          * talloc_realloc just adjusts the size for this.
90          */
91         cli->conn.pending = talloc_realloc(NULL, cli->conn.pending,
92                                            struct tevent_req *,
93                                            num_pending - 1);
94         return;
95 }
96
97 static int smb2cli_req_destructor(struct tevent_req *req)
98 {
99         smb2cli_req_unset_pending(req);
100         return 0;
101 }
102
103 static void smb2cli_inbuf_received(struct tevent_req *subreq);
104
105 static bool smb2cli_req_set_pending(struct tevent_req *req)
106 {
107         struct smb2cli_req_state *state =
108                 tevent_req_data(req,
109                 struct smb2cli_req_state);
110         struct cli_state *cli;
111         struct tevent_req **pending;
112         int num_pending;
113         struct tevent_req *subreq;
114
115         cli = state->cli;
116         num_pending = talloc_array_length(cli->conn.pending);
117
118         pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *,
119                                  num_pending+1);
120         if (pending == NULL) {
121                 return false;
122         }
123         pending[num_pending] = req;
124         cli->conn.pending = pending;
125         talloc_set_destructor(req, smb2cli_req_destructor);
126
127         if (num_pending > 0) {
128                 return true;
129         }
130
131         /*
132          * We're the first ones, add the read_smb request that waits for the
133          * answer from the server
134          */
135         subreq = read_smb_send(cli->conn.pending, state->ev, cli->fd);
136         if (subreq == NULL) {
137                 smb2cli_req_unset_pending(req);
138                 return false;
139         }
140         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
141         return true;
142 }
143
144 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
145 {
146         if (cli->fd != -1) {
147                 close(cli->fd);
148                 cli->fd = -1;
149         }
150
151         /*
152          * Cancel all pending requests. We don't do a for-loop walking
153          * cli->conn.pending because that array changes in
154          * cli_smb_req_destructor().
155          */
156         while (talloc_array_length(cli->conn.pending) > 0) {
157                 struct tevent_req *req;
158                 struct smb2cli_req_state *state;
159
160                 req = cli->conn.pending[0];
161                 state = tevent_req_data(req, struct smb2cli_req_state);
162
163                 smb2cli_req_unset_pending(req);
164
165                 /*
166                  * we need to defer the callback, because we may notify more
167                  * then one caller.
168                  */
169                 tevent_req_defer_callback(req, state->ev);
170                 tevent_req_nterror(req, status);
171         }
172 }
173
174 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
175                                       struct tevent_context *ev,
176                                       struct cli_state *cli,
177                                       uint16_t cmd,
178                                       uint32_t flags,
179                                       const uint8_t *fixed,
180                                       uint16_t fixed_len,
181                                       const uint8_t *dyn,
182                                       uint16_t dyn_len)
183 {
184         struct tevent_req *req;
185         struct smb2cli_req_state *state;
186
187         req = tevent_req_create(mem_ctx, &state,
188                                 struct smb2cli_req_state);
189         if (req == NULL) {
190                 return NULL;
191         }
192         state->ev = ev;
193         state->cli = cli;
194
195         state->recv_iov = talloc_zero_array(state, struct iovec, 3);
196         if (state->recv_iov == NULL) {
197                 TALLOC_FREE(req);
198                 return NULL;
199         }
200
201         state->fixed = fixed;
202         state->fixed_len = fixed_len;
203         state->dyn = dyn;
204         state->dyn_len = dyn_len;
205
206         SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
207         SSVAL(state->hdr, SMB2_HDR_LENGTH,      SMB2_HDR_BODY);
208         SSVAL(state->hdr, SMB2_HDR_EPOCH,       1);
209         SIVAL(state->hdr, SMB2_HDR_STATUS,      NT_STATUS_V(NT_STATUS_OK));
210         SSVAL(state->hdr, SMB2_HDR_OPCODE,      cmd);
211         SSVAL(state->hdr, SMB2_HDR_CREDIT,      31);
212         SIVAL(state->hdr, SMB2_HDR_FLAGS,       flags);
213         SIVAL(state->hdr, SMB2_HDR_PID,         cli->smb2.pid);
214         SIVAL(state->hdr, SMB2_HDR_TID,         cli->smb2.tid);
215         SBVAL(state->hdr, SMB2_HDR_SESSION_ID,  cli->smb2.uid);
216
217         return req;
218 }
219
220 static void smb2cli_writev_done(struct tevent_req *subreq);
221
222 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
223                                      int num_reqs)
224 {
225         struct smb2cli_req_state *state;
226         struct tevent_req *subreq;
227         struct iovec *iov;
228         int i, num_iov, nbt_len;
229
230         /*
231          * 1 for the nbt length
232          * per request: HDR, fixed, dyn, padding
233          * -1 because the last one does not need padding
234          */
235
236         iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
237         if (iov == NULL) {
238                 return NT_STATUS_NO_MEMORY;
239         }
240
241         num_iov = 1;
242         nbt_len = 0;
243
244         for (i=0; i<num_reqs; i++) {
245                 size_t reqlen;
246                 bool ret;
247
248                 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
249
250                 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.mid++);
251
252                 iov[num_iov].iov_base = state->hdr;
253                 iov[num_iov].iov_len  = sizeof(state->hdr);
254                 num_iov += 1;
255
256                 iov[num_iov].iov_base = discard_const(state->fixed);
257                 iov[num_iov].iov_len  = state->fixed_len;
258                 num_iov += 1;
259
260                 if (state->dyn != NULL) {
261                         iov[num_iov].iov_base = discard_const(state->dyn);
262                         iov[num_iov].iov_len  = state->dyn_len;
263                         num_iov += 1;
264                 }
265
266                 reqlen = sizeof(state->hdr) + state->fixed_len +
267                         state->dyn_len;
268
269                 if (i < num_reqs-1) {
270                         if ((reqlen % 8) > 0) {
271                                 uint8_t pad = 8 - (reqlen % 8);
272                                 iov[num_iov].iov_base = state->pad;
273                                 iov[num_iov].iov_len = pad;
274                                 num_iov += 1;
275                                 reqlen += pad;
276                         }
277                         SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
278                 }
279                 nbt_len += reqlen;
280
281                 ret = smb2cli_req_set_pending(reqs[i]);
282                 if (!ret) {
283                         return NT_STATUS_NO_MEMORY;
284                 }
285         }
286
287         /*
288          * TODO: Do signing here
289          */
290
291         state = tevent_req_data(reqs[0], struct smb2cli_req_state);
292         _smb_setlen_large(state->nbt, nbt_len);
293         iov[0].iov_base = state->nbt;
294         iov[0].iov_len  = sizeof(state->nbt);
295
296         subreq = writev_send(state, state->ev, state->cli->conn.outgoing,
297                              state->cli->fd, false, iov, num_iov);
298         if (subreq == NULL) {
299                 return NT_STATUS_NO_MEMORY;
300         }
301         tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
302         return NT_STATUS_OK;
303 }
304
305 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
306                                     struct tevent_context *ev,
307                                     struct cli_state *cli,
308                                     uint16_t cmd,
309                                     uint32_t flags,
310                                     const uint8_t *fixed,
311                                     uint16_t fixed_len,
312                                     const uint8_t *dyn,
313                                     uint16_t dyn_len)
314 {
315         struct tevent_req *req;
316         NTSTATUS status;
317
318         req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags,
319                                  fixed, fixed_len, dyn, dyn_len);
320         if (req == NULL) {
321                 return NULL;
322         }
323         if (!tevent_req_is_in_progress(req)) {
324                 return req;
325         }
326         status = smb2cli_req_compound_submit(&req, 1);
327         if (tevent_req_nterror(req, status)) {
328                 return tevent_req_post(req, ev);
329         }
330         return req;
331 }
332
333 static void smb2cli_writev_done(struct tevent_req *subreq)
334 {
335         struct tevent_req *req =
336                 tevent_req_callback_data(subreq,
337                 struct tevent_req);
338         struct smb2cli_req_state *state =
339                 tevent_req_data(req,
340                 struct smb2cli_req_state);
341         ssize_t nwritten;
342         int err;
343
344         nwritten = writev_recv(subreq, &err);
345         TALLOC_FREE(subreq);
346         if (nwritten == -1) {
347                 /* here, we need to notify all pending requests */
348                 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
349                 return;
350         }
351 }
352
353 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
354                                              struct iovec **piov, int *pnum_iov)
355 {
356         struct iovec *iov;
357         int num_iov;
358         size_t buflen;
359         size_t taken;
360
361         num_iov = 1;
362
363         iov = talloc_array(mem_ctx, struct iovec, num_iov);
364         if (iov == NULL) {
365                 return NT_STATUS_NO_MEMORY;
366         }
367         iov[0].iov_base = buf;
368         iov[0].iov_len = 4;
369
370         buflen = smb_len_large(buf) + 4;
371         taken = 4;
372
373         while (taken < buflen) {
374                 size_t len = buflen - taken;
375                 uint8_t *hdr = buf + taken;
376                 struct iovec *cur;
377                 size_t full_size;
378                 size_t next_command_ofs;
379                 uint16_t body_size;
380                 struct iovec *iov_tmp;
381
382                 /*
383                  * We need the header plus the body length field
384                  */
385
386                 if (len < SMB2_HDR_BODY + 2) {
387                         DEBUG(10, ("%d bytes left, expected at least %d\n",
388                                    (int)len, SMB2_HDR_BODY));
389                         goto inval;
390                 }
391                 if (IVAL(hdr, 0) != SMB2_MAGIC) {
392                         DEBUG(10, ("Got non-SMB2 PDU: %x\n",
393                                    IVAL(hdr, 0)));
394                         goto inval;
395                 }
396                 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
397                         DEBUG(10, ("Got HDR len %d, expected %d\n",
398                                    SVAL(hdr, 4), SMB2_HDR_BODY));
399                         goto inval;
400                 }
401
402                 full_size = len;
403                 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
404                 body_size = SVAL(hdr, SMB2_HDR_BODY);
405
406                 if (next_command_ofs != 0) {
407                         if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
408                                 goto inval;
409                         }
410                         if (next_command_ofs > full_size) {
411                                 goto inval;
412                         }
413                         full_size = next_command_ofs;
414                 }
415                 if (body_size < 2) {
416                         goto inval;
417                 }
418                 body_size &= 0xfffe;
419
420                 if (body_size > (full_size - SMB2_HDR_BODY)) {
421                         goto inval;
422                 }
423
424                 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
425                                          num_iov + 3);
426                 if (iov_tmp == NULL) {
427                         TALLOC_FREE(iov);
428                         return NT_STATUS_NO_MEMORY;
429                 }
430                 iov = iov_tmp;
431                 cur = &iov[num_iov];
432                 num_iov += 3;
433
434                 cur[0].iov_base = hdr;
435                 cur[0].iov_len  = SMB2_HDR_BODY;
436                 cur[1].iov_base = hdr + SMB2_HDR_BODY;
437                 cur[1].iov_len  = body_size;
438                 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
439                 cur[2].iov_len  = full_size - (SMB2_HDR_BODY + body_size);
440
441                 taken += full_size;
442         }
443
444         *piov = iov;
445         *pnum_iov = num_iov;
446         return NT_STATUS_OK;
447
448 inval:
449         TALLOC_FREE(iov);
450         return NT_STATUS_INVALID_NETWORK_RESPONSE;
451 }
452
453 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
454                                                 uint64_t mid)
455 {
456         int num_pending = talloc_array_length(cli->conn.pending);
457         int i;
458
459         for (i=0; i<num_pending; i++) {
460                 struct tevent_req *req = cli->conn.pending[i];
461                 struct smb2cli_req_state *state =
462                         tevent_req_data(req,
463                         struct smb2cli_req_state);
464
465                 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
466                         return req;
467                 }
468         }
469         return NULL;
470 }
471
472 static void smb2cli_inbuf_received(struct tevent_req *subreq)
473 {
474         struct cli_state *cli =
475                 tevent_req_callback_data(subreq,
476                 struct cli_state);
477         TALLOC_CTX *frame = talloc_stackframe();
478         struct tevent_req *req;
479         struct smb2cli_req_state *state;
480         struct iovec *iov;
481         int i, num_iov;
482         NTSTATUS status;
483         uint8_t *inbuf;
484         ssize_t received;
485         int err;
486         size_t num_pending;
487
488         received = read_smb_recv(subreq, frame, &inbuf, &err);
489         TALLOC_FREE(subreq);
490         if (received == -1) {
491                 /*
492                  * We need to close the connection and notify
493                  * all pending requests.
494                  */
495                 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
496                 TALLOC_FREE(frame);
497                 return;
498         }
499
500         status = smb2cli_inbuf_parse_compound(inbuf, frame,
501                                               &iov, &num_iov);
502         if (!NT_STATUS_IS_OK(status)) {
503                 /*
504                  * if we cannot parse the incoming pdu,
505                  * the connection becomes unusable.
506                  *
507                  * We need to close the connection and notify
508                  * all pending requests.
509                  */
510                 smb2cli_notify_pending(cli, status);
511                 TALLOC_FREE(frame);
512                 return;
513         }
514
515         for (i=1; i<num_iov; i+=3) {
516                 uint8_t *inbuf_ref = NULL;
517                 struct iovec *cur = &iov[i];
518                 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
519
520                 req = cli_smb2_find_pending(
521                         cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
522                 if (req == NULL) {
523                         /*
524                          * TODO: handle oplock breaks and async responses
525                          */
526
527                         /*
528                          * We need to close the connection and notify
529                          * all pending requests.
530                          */
531                         smb2cli_notify_pending(cli, status);
532                         TALLOC_FREE(frame);
533                         return;
534                 }
535                 smb2cli_req_unset_pending(req);
536                 state = tevent_req_data(req, struct smb2cli_req_state);
537
538                 /*
539                  * There might be more than one response
540                  * we need to defer the notifications
541                  */
542                 tevent_req_defer_callback(req, state->ev);
543
544                 /*
545                  * Note: here we use talloc_reference() in a way
546                  *       that does not expose it to the caller.
547                  */
548                 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
549                 if (tevent_req_nomem(inbuf_ref, req)) {
550                         continue;
551                 }
552
553                 /* copy the related buffers */
554                 state->recv_iov[0] = cur[0];
555                 state->recv_iov[1] = cur[1];
556                 state->recv_iov[2] = cur[2];
557
558                 tevent_req_done(req);
559         }
560
561         TALLOC_FREE(frame);
562
563         num_pending = talloc_array_length(cli->conn.pending);
564         if (num_pending == 0) {
565                 /* no more pending requests, so we are done for now */
566                 return;
567         }
568         req = cli->conn.pending[0];
569         state = tevent_req_data(req, struct smb2cli_req_state);
570
571         /*
572          * add the read_smb request that waits for the
573          * next answer from the server
574          */
575         subreq = read_smb_send(cli->conn.pending, state->ev, cli->fd);
576         if (subreq == NULL) {
577                 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
578                 return;
579         }
580         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
581 }
582
583 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
584                           struct iovec **piov, int body_size)
585 {
586         struct smb2cli_req_state *state =
587                 tevent_req_data(req,
588                 struct smb2cli_req_state);
589         NTSTATUS status;
590
591         if (tevent_req_is_nterror(req, &status)) {
592                 return status;
593         }
594
595         status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
596
597         if (body_size != 0) {
598                 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
599                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
600                 }
601         }
602         if (piov != NULL) {
603                 *piov = talloc_move(mem_ctx, &state->recv_iov);
604         }
605
606         return status;
607 }