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