s3: Fix async smb handling
[idra/samba.git] / source3 / libsmb / async_smb.c
1 /*
2    Unix SMB/CIFS implementation.
3    Infrastructure for async SMB client requests
4    Copyright (C) Volker Lendecke 2008
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 "libsmb/libsmb.h"
22 #include "../lib/async_req/async_sock.h"
23 #include "../lib/util/tevent_ntstatus.h"
24 #include "../lib/util/tevent_unix.h"
25 #include "async_smb.h"
26 #include "smb_crypt.h"
27 #include "libsmb/nmblib.h"
28 #include "read_smb.h"
29
30 static NTSTATUS cli_pull_raw_error(const uint8_t *buf)
31 {
32         uint32_t flags2 = SVAL(buf, smb_flg2);
33
34         if (flags2 & FLAGS2_32_BIT_ERROR_CODES) {
35                 return NT_STATUS(IVAL(buf, smb_rcls));
36         }
37
38         return NT_STATUS_DOS(CVAL(buf, smb_rcls), SVAL(buf,smb_err));
39 }
40
41 /**
42  * Figure out if there is an andx command behind the current one
43  * @param[in] buf       The smb buffer to look at
44  * @param[in] ofs       The offset to the wct field that is followed by the cmd
45  * @retval Is there a command following?
46  */
47
48 static bool have_andx_command(const char *buf, uint16_t ofs)
49 {
50         uint8_t wct;
51         size_t buflen = talloc_get_size(buf);
52
53         if ((ofs == buflen-1) || (ofs == buflen)) {
54                 return false;
55         }
56
57         wct = CVAL(buf, ofs);
58         if (wct < 2) {
59                 /*
60                  * Not enough space for the command and a following pointer
61                  */
62                 return false;
63         }
64         return (CVAL(buf, ofs+1) != 0xff);
65 }
66
67 #define MAX_SMB_IOV 5
68
69 struct cli_smb_state {
70         struct tevent_context *ev;
71         struct cli_state *cli;
72         uint8_t header[smb_wct+1]; /* Space for the header including the wct */
73
74         /*
75          * For normal requests, cli_smb_req_send chooses a mid. Secondary
76          * trans requests need to use the mid of the primary request, so we
77          * need a place to store it. Assume it's set if != 0.
78          */
79         uint16_t mid;
80
81         uint16_t *vwv;
82         uint8_t bytecount_buf[2];
83
84         struct iovec iov[MAX_SMB_IOV+3];
85         int iov_count;
86
87         uint8_t *inbuf;
88         uint32_t seqnum;
89         int chain_num;
90         int chain_length;
91         struct tevent_req **chained_requests;
92 };
93
94 static uint16_t cli_alloc_mid(struct cli_state *cli)
95 {
96         int num_pending = talloc_array_length(cli->conn.pending);
97         uint16_t result;
98
99         while (true) {
100                 int i;
101
102                 result = cli->smb1.mid++;
103                 if ((result == 0) || (result == 0xffff)) {
104                         continue;
105                 }
106
107                 for (i=0; i<num_pending; i++) {
108                         if (result == cli_smb_req_mid(cli->conn.pending[i])) {
109                                 break;
110                         }
111                 }
112
113                 if (i == num_pending) {
114                         return result;
115                 }
116         }
117 }
118
119 void cli_smb_req_unset_pending(struct tevent_req *req)
120 {
121         struct cli_smb_state *state = tevent_req_data(
122                 req, struct cli_smb_state);
123         struct cli_state *cli = state->cli;
124         int num_pending = talloc_array_length(cli->conn.pending);
125         int i;
126
127         if (state->mid != 0) {
128                 /*
129                  * This is a [nt]trans[2] request which waits
130                  * for more than one reply.
131                  */
132                 return;
133         }
134
135         if (num_pending == 1) {
136                 /*
137                  * The pending read_smb tevent_req is a child of
138                  * cli->pending. So if nothing is pending anymore, we need to
139                  * delete the socket read fde.
140                  */
141                 TALLOC_FREE(cli->conn.pending);
142                 cli->conn.read_smb_req = NULL;
143                 return;
144         }
145
146         for (i=0; i<num_pending; i++) {
147                 if (req == cli->conn.pending[i]) {
148                         break;
149                 }
150         }
151         if (i == num_pending) {
152                 /*
153                  * Something's seriously broken. Just returning here is the
154                  * right thing nevertheless, the point of this routine is to
155                  * remove ourselves from cli->conn.pending.
156                  */
157                 return;
158         }
159
160         /*
161          * Remove ourselves from the cli->conn.pending array
162          */
163         cli->conn.pending[i] = cli->conn.pending[num_pending-1];
164
165         /*
166          * No NULL check here, we're shrinking by sizeof(void *), and
167          * talloc_realloc just adjusts the size for this.
168          */
169         cli->conn.pending = talloc_realloc(NULL, cli->conn.pending,
170                                            struct tevent_req *,
171                                            num_pending - 1);
172         return;
173 }
174
175 static int cli_smb_req_destructor(struct tevent_req *req)
176 {
177         struct cli_smb_state *state = tevent_req_data(
178                 req, struct cli_smb_state);
179         /*
180          * Make sure we really remove it from
181          * the pending array on destruction.
182          */
183         state->mid = 0;
184         cli_smb_req_unset_pending(req);
185         return 0;
186 }
187
188 static void cli_smb_received(struct tevent_req *subreq);
189
190 bool cli_smb_req_set_pending(struct tevent_req *req)
191 {
192         struct cli_smb_state *state = tevent_req_data(
193                 req, struct cli_smb_state);
194         struct cli_state *cli;
195         struct tevent_req **pending;
196         int num_pending;
197
198         cli = state->cli;
199         num_pending = talloc_array_length(cli->conn.pending);
200
201         pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *,
202                                  num_pending+1);
203         if (pending == NULL) {
204                 return false;
205         }
206         pending[num_pending] = req;
207         cli->conn.pending = pending;
208         talloc_set_destructor(req, cli_smb_req_destructor);
209
210         if (cli->conn.read_smb_req != NULL) {
211                 return true;
212         }
213
214         /*
215          * We're the first ones, add the read_smb request that waits for the
216          * answer from the server
217          */
218         cli->conn.read_smb_req = read_smb_send(cli->conn.pending, state->ev,
219                                                cli->conn.fd);
220         if (cli->conn.read_smb_req == NULL) {
221                 cli_smb_req_unset_pending(req);
222                 return false;
223         }
224         tevent_req_set_callback(cli->conn.read_smb_req, cli_smb_received, cli);
225         return true;
226 }
227
228 /*
229  * Fetch a smb request's mid. Only valid after the request has been sent by
230  * cli_smb_req_send().
231  */
232 uint16_t cli_smb_req_mid(struct tevent_req *req)
233 {
234         struct cli_smb_state *state = tevent_req_data(
235                 req, struct cli_smb_state);
236         return SVAL(state->header, smb_mid);
237 }
238
239 void cli_smb_req_set_mid(struct tevent_req *req, uint16_t mid)
240 {
241         struct cli_smb_state *state = tevent_req_data(
242                 req, struct cli_smb_state);
243         state->mid = mid;
244 }
245
246 uint32_t cli_smb_req_seqnum(struct tevent_req *req)
247 {
248         struct cli_smb_state *state = tevent_req_data(
249                 req, struct cli_smb_state);
250         return state->seqnum;
251 }
252
253 void cli_smb_req_set_seqnum(struct tevent_req *req, uint32_t seqnum)
254 {
255         struct cli_smb_state *state = tevent_req_data(
256                 req, struct cli_smb_state);
257         state->seqnum = seqnum;
258 }
259
260 static size_t iov_len(const struct iovec *iov, int count)
261 {
262         size_t result = 0;
263         int i;
264         for (i=0; i<count; i++) {
265                 result += iov[i].iov_len;
266         }
267         return result;
268 }
269
270 static uint8_t *iov_concat(TALLOC_CTX *mem_ctx, const struct iovec *iov,
271                            int count)
272 {
273         size_t len = iov_len(iov, count);
274         size_t copied;
275         uint8_t *buf;
276         int i;
277
278         buf = talloc_array(mem_ctx, uint8_t, len);
279         if (buf == NULL) {
280                 return NULL;
281         }
282         copied = 0;
283         for (i=0; i<count; i++) {
284                 memcpy(buf+copied, iov[i].iov_base, iov[i].iov_len);
285                 copied += iov[i].iov_len;
286         }
287         return buf;
288 }
289
290 struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx,
291                                       struct event_context *ev,
292                                       struct cli_state *cli,
293                                       uint8_t smb_command,
294                                       uint8_t additional_flags,
295                                       uint8_t wct, uint16_t *vwv,
296                                       int iov_count,
297                                       struct iovec *bytes_iov)
298 {
299         struct tevent_req *result;
300         struct cli_smb_state *state;
301         struct timeval endtime;
302
303         if (iov_count > MAX_SMB_IOV) {
304                 /*
305                  * Should not happen :-)
306                  */
307                 return NULL;
308         }
309
310         result = tevent_req_create(mem_ctx, &state, struct cli_smb_state);
311         if (result == NULL) {
312                 return NULL;
313         }
314         state->ev = ev;
315         state->cli = cli;
316         state->mid = 0;         /* Set to auto-choose in cli_smb_req_send */
317         state->chain_num = 0;
318         state->chained_requests = NULL;
319
320         cli_setup_packet_buf(cli, (char *)state->header);
321         SCVAL(state->header, smb_com, smb_command);
322         SSVAL(state->header, smb_tid, cli->smb1.tid);
323         SCVAL(state->header, smb_wct, wct);
324
325         state->vwv = vwv;
326
327         SSVAL(state->bytecount_buf, 0, iov_len(bytes_iov, iov_count));
328
329         state->iov[0].iov_base = (void *)state->header;
330         state->iov[0].iov_len  = sizeof(state->header);
331         state->iov[1].iov_base = (void *)state->vwv;
332         state->iov[1].iov_len  = wct * sizeof(uint16_t);
333         state->iov[2].iov_base = (void *)state->bytecount_buf;
334         state->iov[2].iov_len  = sizeof(uint16_t);
335
336         if (iov_count != 0) {
337                 memcpy(&state->iov[3], bytes_iov,
338                        iov_count * sizeof(*bytes_iov));
339         }
340         state->iov_count = iov_count + 3;
341
342         if (cli->timeout) {
343                 endtime = timeval_current_ofs_msec(cli->timeout);
344                 if (!tevent_req_set_endtime(result, ev, endtime)) {
345                         tevent_req_oom(result);
346                 }
347         }
348         return result;
349 }
350
351 static NTSTATUS cli_signv(struct cli_state *cli, struct iovec *iov, int count,
352                           uint32_t *seqnum)
353 {
354         uint8_t *buf;
355
356         /*
357          * Obvious optimization: Make cli_calculate_sign_mac work with struct
358          * iovec directly. MD5Update would do that just fine.
359          */
360
361         if ((count <= 0) || (iov[0].iov_len < smb_wct)) {
362                 return NT_STATUS_INVALID_PARAMETER;
363         }
364
365         buf = iov_concat(talloc_tos(), iov, count);
366         if (buf == NULL) {
367                 return NT_STATUS_NO_MEMORY;
368         }
369
370         cli_calculate_sign_mac(cli, (char *)buf, seqnum);
371         memcpy(iov[0].iov_base, buf, iov[0].iov_len);
372
373         TALLOC_FREE(buf);
374         return NT_STATUS_OK;
375 }
376
377 static void cli_smb_sent(struct tevent_req *subreq);
378
379 static NTSTATUS cli_smb_req_iov_send(struct tevent_req *req,
380                                      struct cli_smb_state *state,
381                                      struct iovec *iov, int iov_count)
382 {
383         struct tevent_req *subreq;
384         NTSTATUS status;
385
386         if (!cli_state_is_connected(state->cli)) {
387                 return NT_STATUS_CONNECTION_INVALID;
388         }
389
390         if (iov[0].iov_len < smb_wct) {
391                 return NT_STATUS_INVALID_PARAMETER;
392         }
393
394         if (state->mid != 0) {
395                 SSVAL(iov[0].iov_base, smb_mid, state->mid);
396         } else {
397                 uint16_t mid = cli_alloc_mid(state->cli);
398                 SSVAL(iov[0].iov_base, smb_mid, mid);
399         }
400
401         smb_setlen((char *)iov[0].iov_base, iov_len(iov, iov_count) - 4);
402
403         status = cli_signv(state->cli, iov, iov_count, &state->seqnum);
404
405         if (!NT_STATUS_IS_OK(status)) {
406                 return status;
407         }
408
409         if (cli_encryption_on(state->cli)) {
410                 char *buf, *enc_buf;
411
412                 buf = (char *)iov_concat(talloc_tos(), iov, iov_count);
413                 if (buf == NULL) {
414                         return NT_STATUS_NO_MEMORY;
415                 }
416                 status = common_encrypt_buffer(state->cli->trans_enc_state,
417                                                (char *)buf, &enc_buf);
418                 TALLOC_FREE(buf);
419                 if (!NT_STATUS_IS_OK(status)) {
420                         DEBUG(0, ("Error in encrypting client message: %s\n",
421                                   nt_errstr(status)));
422                         return status;
423                 }
424                 buf = (char *)talloc_memdup(state, enc_buf,
425                                             smb_len(enc_buf)+4);
426                 SAFE_FREE(enc_buf);
427                 if (buf == NULL) {
428                         return NT_STATUS_NO_MEMORY;
429                 }
430                 iov[0].iov_base = (void *)buf;
431                 iov[0].iov_len = talloc_get_size(buf);
432                 iov_count = 1;
433         }
434         subreq = writev_send(state, state->ev, state->cli->conn.outgoing,
435                              state->cli->conn.fd, false, iov, iov_count);
436         if (subreq == NULL) {
437                 return NT_STATUS_NO_MEMORY;
438         }
439         tevent_req_set_callback(subreq, cli_smb_sent, req);
440         return NT_STATUS_OK;
441 }
442
443 NTSTATUS cli_smb_req_send(struct tevent_req *req)
444 {
445         struct cli_smb_state *state = tevent_req_data(
446                 req, struct cli_smb_state);
447
448         return cli_smb_req_iov_send(req, state, state->iov, state->iov_count);
449 }
450
451 struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx,
452                                 struct event_context *ev,
453                                 struct cli_state *cli,
454                                 uint8_t smb_command,
455                                 uint8_t additional_flags,
456                                 uint8_t wct, uint16_t *vwv,
457                                 uint32_t num_bytes,
458                                 const uint8_t *bytes)
459 {
460         struct tevent_req *req;
461         struct iovec iov;
462         NTSTATUS status;
463
464         iov.iov_base = discard_const_p(void, bytes);
465         iov.iov_len = num_bytes;
466
467         req = cli_smb_req_create(mem_ctx, ev, cli, smb_command,
468                                  additional_flags, wct, vwv, 1, &iov);
469         if (req == NULL) {
470                 return NULL;
471         }
472
473         status = cli_smb_req_send(req);
474         if (!NT_STATUS_IS_OK(status)) {
475                 tevent_req_nterror(req, status);
476                 return tevent_req_post(req, ev);
477         }
478         return req;
479 }
480
481 static void cli_smb_sent(struct tevent_req *subreq)
482 {
483         struct tevent_req *req = tevent_req_callback_data(
484                 subreq, struct tevent_req);
485         struct cli_smb_state *state = tevent_req_data(
486                 req, struct cli_smb_state);
487         ssize_t nwritten;
488         int err;
489
490         nwritten = writev_recv(subreq, &err);
491         TALLOC_FREE(subreq);
492         if (nwritten == -1) {
493                 cli_state_disconnect(state->cli);
494                 tevent_req_nterror(req, map_nt_error_from_unix(err));
495                 return;
496         }
497
498         switch (CVAL(state->header, smb_com)) {
499         case SMBtranss:
500         case SMBtranss2:
501         case SMBnttranss:
502         case SMBntcancel:
503                 state->inbuf = NULL;
504                 tevent_req_done(req);
505                 return;
506         case SMBlockingX:
507                 if ((CVAL(state->header, smb_wct) == 8) &&
508                     (CVAL(state->vwv+3, 0) == LOCKING_ANDX_OPLOCK_RELEASE)) {
509                         state->inbuf = NULL;
510                         tevent_req_done(req);
511                         return;
512                 }
513         }
514
515         if (!cli_smb_req_set_pending(req)) {
516                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
517                 return;
518         }
519 }
520
521 static void cli_smb_received(struct tevent_req *subreq)
522 {
523         struct cli_state *cli = tevent_req_callback_data(
524                 subreq, struct cli_state);
525         struct tevent_req *req;
526         struct cli_smb_state *state;
527         NTSTATUS status;
528         uint8_t *inbuf;
529         ssize_t received;
530         int num_pending;
531         int i, err;
532         uint16_t mid;
533         bool oplock_break;
534
535         if (subreq != cli->conn.read_smb_req) {
536                 DEBUG(1, ("Internal error: cli_smb_received called with "
537                           "unexpected subreq\n"));
538                 status = NT_STATUS_INTERNAL_ERROR;
539                 goto fail;
540         }
541
542         received = read_smb_recv(subreq, talloc_tos(), &inbuf, &err);
543         TALLOC_FREE(subreq);
544         cli->conn.read_smb_req = NULL;
545         if (received == -1) {
546                 cli_state_disconnect(cli);
547                 status = map_nt_error_from_unix(err);
548                 goto fail;
549         }
550
551         if ((IVAL(inbuf, 4) != 0x424d53ff) /* 0xFF"SMB" */
552             && (SVAL(inbuf, 4) != 0x45ff)) /* 0xFF"E" */ {
553                 DEBUG(10, ("Got non-SMB PDU\n"));
554                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
555                 goto fail;
556         }
557
558         if (cli_encryption_on(cli) && (CVAL(inbuf, 0) == 0)) {
559                 uint16_t enc_ctx_num;
560
561                 status = get_enc_ctx_num(inbuf, &enc_ctx_num);
562                 if (!NT_STATUS_IS_OK(status)) {
563                         DEBUG(10, ("get_enc_ctx_num returned %s\n",
564                                    nt_errstr(status)));
565                         goto fail;
566                 }
567
568                 if (enc_ctx_num != cli->trans_enc_state->enc_ctx_num) {
569                         DEBUG(10, ("wrong enc_ctx %d, expected %d\n",
570                                    enc_ctx_num,
571                                    cli->trans_enc_state->enc_ctx_num));
572                         status = NT_STATUS_INVALID_HANDLE;
573                         goto fail;
574                 }
575
576                 status = common_decrypt_buffer(cli->trans_enc_state,
577                                                (char *)inbuf);
578                 if (!NT_STATUS_IS_OK(status)) {
579                         DEBUG(10, ("common_decrypt_buffer returned %s\n",
580                                    nt_errstr(status)));
581                         goto fail;
582                 }
583         }
584
585         mid = SVAL(inbuf, smb_mid);
586         num_pending = talloc_array_length(cli->conn.pending);
587
588         for (i=0; i<num_pending; i++) {
589                 if (mid == cli_smb_req_mid(cli->conn.pending[i])) {
590                         break;
591                 }
592         }
593         if (i == num_pending) {
594                 /* Dump unexpected reply */
595                 TALLOC_FREE(inbuf);
596                 goto done;
597         }
598
599         oplock_break = false;
600
601         if (mid == 0xffff) {
602                 /*
603                  * Paranoia checks that this is really an oplock break request.
604                  */
605                 oplock_break = (smb_len(inbuf) == 51); /* hdr + 8 words */
606                 oplock_break &= ((CVAL(inbuf, smb_flg) & FLAG_REPLY) == 0);
607                 oplock_break &= (CVAL(inbuf, smb_com) == SMBlockingX);
608                 oplock_break &= (SVAL(inbuf, smb_vwv6) == 0);
609                 oplock_break &= (SVAL(inbuf, smb_vwv7) == 0);
610
611                 if (!oplock_break) {
612                         /* Dump unexpected reply */
613                         TALLOC_FREE(inbuf);
614                         goto done;
615                 }
616         }
617
618         req = cli->conn.pending[i];
619         state = tevent_req_data(req, struct cli_smb_state);
620
621         if (!oplock_break /* oplock breaks are not signed */
622             && !cli_check_sign_mac(cli, (char *)inbuf, state->seqnum+1)) {
623                 DEBUG(10, ("cli_check_sign_mac failed\n"));
624                 TALLOC_FREE(inbuf);
625                 status = NT_STATUS_ACCESS_DENIED;
626                 cli_state_disconnect(cli);
627                 goto fail;
628         }
629
630         if (state->chained_requests == NULL) {
631                 state->inbuf = talloc_move(state, &inbuf);
632                 talloc_set_destructor(req, NULL);
633                 cli_smb_req_unset_pending(req);
634                 state->chain_num = 0;
635                 state->chain_length = 1;
636                 tevent_req_done(req);
637         } else {
638                 struct tevent_req **chain = talloc_move(
639                         talloc_tos(), &state->chained_requests);
640                 int num_chained = talloc_array_length(chain);
641
642                 for (i=0; i<num_chained; i++) {
643                         state = tevent_req_data(chain[i], struct
644                                                 cli_smb_state);
645                         state->inbuf = inbuf;
646                         state->chain_num = i;
647                         state->chain_length = num_chained;
648                         tevent_req_done(chain[i]);
649                 }
650                 TALLOC_FREE(inbuf);
651                 TALLOC_FREE(chain);
652         }
653  done:
654         if ((talloc_array_length(cli->conn.pending) > 0) &&
655             (cli->conn.read_smb_req == NULL)) {
656                 /*
657                  * Set up another read request for the other pending cli_smb
658                  * requests
659                  */
660                 state = tevent_req_data(cli->conn.pending[0],
661                                         struct cli_smb_state);
662                 cli->conn.read_smb_req = read_smb_send(
663                         cli->conn.pending, state->ev, cli->conn.fd);
664                 if (cli->conn.read_smb_req == NULL) {
665                         status = NT_STATUS_NO_MEMORY;
666                         goto fail;
667                 }
668                 tevent_req_set_callback(cli->conn.read_smb_req,
669                                         cli_smb_received, cli);
670         }
671         return;
672  fail:
673         /*
674          * Cancel all pending requests. We don't do a for-loop walking
675          * cli->conn.pending because that array changes in
676          * cli_smb_req_destructor().
677          */
678         while (talloc_array_length(cli->conn.pending) > 0) {
679                 req = cli->conn.pending[0];
680                 talloc_set_destructor(req, NULL);
681                 cli_smb_req_unset_pending(req);
682                 tevent_req_nterror(req, status);
683         }
684 }
685
686 NTSTATUS cli_smb_recv(struct tevent_req *req,
687                       TALLOC_CTX *mem_ctx, uint8_t **pinbuf,
688                       uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv,
689                       uint32_t *pnum_bytes, uint8_t **pbytes)
690 {
691         struct cli_smb_state *state = tevent_req_data(
692                 req, struct cli_smb_state);
693         NTSTATUS status = NT_STATUS_OK;
694         uint8_t cmd, wct;
695         uint16_t num_bytes;
696         size_t wct_ofs, bytes_offset;
697         int i;
698
699         if (tevent_req_is_nterror(req, &status)) {
700                 return status;
701         }
702
703         if (state->inbuf == NULL) {
704                 if (min_wct != 0) {
705                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
706                 }
707                 if (pinbuf) {
708                         *pinbuf = NULL;
709                 }
710                 if (pwct) {
711                         *pwct = 0;
712                 }
713                 if (pvwv) {
714                         *pvwv = NULL;
715                 }
716                 if (pnum_bytes) {
717                         *pnum_bytes = 0;
718                 }
719                 if (pbytes) {
720                         *pbytes = NULL;
721                 }
722                 /* This was a request without a reply */
723                 return NT_STATUS_OK;
724         }
725
726         wct_ofs = smb_wct;
727         cmd = CVAL(state->inbuf, smb_com);
728
729         for (i=0; i<state->chain_num; i++) {
730                 if (i < state->chain_num-1) {
731                         if (cmd == 0xff) {
732                                 return NT_STATUS_REQUEST_ABORTED;
733                         }
734                         if (!is_andx_req(cmd)) {
735                                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
736                         }
737                 }
738
739                 if (!have_andx_command((char *)state->inbuf, wct_ofs)) {
740                         /*
741                          * This request was not completed because a previous
742                          * request in the chain had received an error.
743                          */
744                         return NT_STATUS_REQUEST_ABORTED;
745                 }
746
747                 wct_ofs = SVAL(state->inbuf, wct_ofs + 3);
748
749                 /*
750                  * Skip the all-present length field. No overflow, we've just
751                  * put a 16-bit value into a size_t.
752                  */
753                 wct_ofs += 4;
754
755                 if (wct_ofs+2 > talloc_get_size(state->inbuf)) {
756                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
757                 }
758
759                 cmd = CVAL(state->inbuf, wct_ofs + 1);
760         }
761
762         state->cli->raw_status = cli_pull_raw_error(state->inbuf);
763         if (NT_STATUS_IS_DOS(state->cli->raw_status)) {
764                 uint8_t eclass = NT_STATUS_DOS_CLASS(state->cli->raw_status);
765                 uint16_t ecode = NT_STATUS_DOS_CODE(state->cli->raw_status);
766                 /*
767                  * TODO: is it really a good idea to do a mapping here?
768                  *
769                  * The old cli_pull_error() also does it, so I do not change
770                  * the behavior yet.
771                  */
772                 status = dos_to_ntstatus(eclass, ecode);
773         } else {
774                 status = state->cli->raw_status;
775         }
776
777         if (!have_andx_command((char *)state->inbuf, wct_ofs)) {
778
779                 if ((cmd == SMBsesssetupX)
780                     && NT_STATUS_EQUAL(
781                             status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
782                         /*
783                          * NT_STATUS_MORE_PROCESSING_REQUIRED is a
784                          * valid return code for session setup
785                          */
786                         goto no_err;
787                 }
788
789                 if (NT_STATUS_IS_ERR(status)) {
790                         /*
791                          * The last command takes the error code. All
792                          * further commands down the requested chain
793                          * will get a NT_STATUS_REQUEST_ABORTED.
794                          */
795                         return status;
796                 }
797         }
798
799 no_err:
800
801         wct = CVAL(state->inbuf, wct_ofs);
802         bytes_offset = wct_ofs + 1 + wct * sizeof(uint16_t);
803         num_bytes = SVAL(state->inbuf, bytes_offset);
804
805         if (wct < min_wct) {
806                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
807         }
808
809         /*
810          * wct_ofs is a 16-bit value plus 4, wct is a 8-bit value, num_bytes
811          * is a 16-bit value. So bytes_offset being size_t should be far from
812          * wrapping.
813          */
814         if ((bytes_offset + 2 > talloc_get_size(state->inbuf))
815             || (bytes_offset > 0xffff)) {
816                 return NT_STATUS_INVALID_NETWORK_RESPONSE;
817         }
818
819         if (pwct != NULL) {
820                 *pwct = wct;
821         }
822         if (pvwv != NULL) {
823                 *pvwv = (uint16_t *)(state->inbuf + wct_ofs + 1);
824         }
825         if (pnum_bytes != NULL) {
826                 *pnum_bytes = num_bytes;
827         }
828         if (pbytes != NULL) {
829                 *pbytes = (uint8_t *)state->inbuf + bytes_offset + 2;
830         }
831         if ((mem_ctx != NULL) && (pinbuf != NULL)) {
832                 if (state->chain_num == state->chain_length-1) {
833                         *pinbuf = talloc_move(mem_ctx, &state->inbuf);
834                 } else {
835                         *pinbuf = state->inbuf;
836                 }
837         }
838
839         return status;
840 }
841
842 size_t cli_smb_wct_ofs(struct tevent_req **reqs, int num_reqs)
843 {
844         size_t wct_ofs;
845         int i;
846
847         wct_ofs = smb_wct - 4;
848
849         for (i=0; i<num_reqs; i++) {
850                 struct cli_smb_state *state;
851                 state = tevent_req_data(reqs[i], struct cli_smb_state);
852                 wct_ofs += iov_len(state->iov+1, state->iov_count-1);
853                 wct_ofs = (wct_ofs + 3) & ~3;
854         }
855         return wct_ofs;
856 }
857
858 NTSTATUS cli_smb_chain_send(struct tevent_req **reqs, int num_reqs)
859 {
860         struct cli_smb_state *first_state = tevent_req_data(
861                 reqs[0], struct cli_smb_state);
862         struct cli_smb_state *last_state = tevent_req_data(
863                 reqs[num_reqs-1], struct cli_smb_state);
864         struct cli_smb_state *state;
865         size_t wct_offset;
866         size_t chain_padding = 0;
867         int i, iovlen;
868         struct iovec *iov = NULL;
869         struct iovec *this_iov;
870         NTSTATUS status;
871
872         iovlen = 0;
873         for (i=0; i<num_reqs; i++) {
874                 state = tevent_req_data(reqs[i], struct cli_smb_state);
875                 iovlen += state->iov_count;
876         }
877
878         iov = talloc_array(last_state, struct iovec, iovlen);
879         if (iov == NULL) {
880                 return NT_STATUS_NO_MEMORY;
881         }
882
883         first_state->chained_requests = (struct tevent_req **)talloc_memdup(
884                 last_state, reqs, sizeof(*reqs) * num_reqs);
885         if (first_state->chained_requests == NULL) {
886                 TALLOC_FREE(iov);
887                 return NT_STATUS_NO_MEMORY;
888         }
889
890         wct_offset = smb_wct - 4;
891         this_iov = iov;
892
893         for (i=0; i<num_reqs; i++) {
894                 size_t next_padding = 0;
895                 uint16_t *vwv;
896
897                 state = tevent_req_data(reqs[i], struct cli_smb_state);
898
899                 if (i < num_reqs-1) {
900                         if (!is_andx_req(CVAL(state->header, smb_com))
901                             || CVAL(state->header, smb_wct) < 2) {
902                                 TALLOC_FREE(iov);
903                                 TALLOC_FREE(first_state->chained_requests);
904                                 return NT_STATUS_INVALID_PARAMETER;
905                         }
906                 }
907
908                 wct_offset += iov_len(state->iov+1, state->iov_count-1) + 1;
909                 if ((wct_offset % 4) != 0) {
910                         next_padding = 4 - (wct_offset % 4);
911                 }
912                 wct_offset += next_padding;
913                 vwv = state->vwv;
914
915                 if (i < num_reqs-1) {
916                         struct cli_smb_state *next_state = tevent_req_data(
917                                 reqs[i+1], struct cli_smb_state);
918                         SCVAL(vwv+0, 0, CVAL(next_state->header, smb_com));
919                         SCVAL(vwv+0, 1, 0);
920                         SSVAL(vwv+1, 0, wct_offset);
921                 } else if (is_andx_req(CVAL(state->header, smb_com))) {
922                         /* properly end the chain */
923                         SCVAL(vwv+0, 0, 0xff);
924                         SCVAL(vwv+0, 1, 0xff);
925                         SSVAL(vwv+1, 0, 0);
926                 }
927
928                 if (i == 0) {
929                         this_iov[0] = state->iov[0];
930                 } else {
931                         /*
932                          * This one is a bit subtle. We have to add
933                          * chain_padding bytes between the requests, and we
934                          * have to also include the wct field of the
935                          * subsequent requests. We use the subsequent header
936                          * for the padding, it contains the wct field in its
937                          * last byte.
938                          */
939                         this_iov[0].iov_len = chain_padding+1;
940                         this_iov[0].iov_base = (void *)&state->header[
941                                 sizeof(state->header) - this_iov[0].iov_len];
942                         memset(this_iov[0].iov_base, 0, this_iov[0].iov_len-1);
943                 }
944                 memcpy(this_iov+1, state->iov+1,
945                        sizeof(struct iovec) * (state->iov_count-1));
946                 this_iov += state->iov_count;
947                 chain_padding = next_padding;
948         }
949
950         status = cli_smb_req_iov_send(reqs[0], last_state, iov, iovlen);
951         if (!NT_STATUS_IS_OK(status)) {
952                 TALLOC_FREE(iov);
953                 TALLOC_FREE(first_state->chained_requests);
954                 return status;
955         }
956
957         return NT_STATUS_OK;
958 }
959
960 bool cli_has_async_calls(struct cli_state *cli)
961 {
962         return ((tevent_queue_length(cli->conn.outgoing) != 0)
963                 || (talloc_array_length(cli->conn.pending) != 0));
964 }
965
966 struct cli_smb_oplock_break_waiter_state {
967         uint16_t fnum;
968         uint8_t level;
969 };
970
971 static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq);
972
973 struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx,
974                                                     struct event_context *ev,
975                                                     struct cli_state *cli)
976 {
977         struct tevent_req *req, *subreq;
978         struct cli_smb_oplock_break_waiter_state *state;
979         struct cli_smb_state *smb_state;
980
981         req = tevent_req_create(mem_ctx, &state,
982                                 struct cli_smb_oplock_break_waiter_state);
983         if (req == NULL) {
984                 return NULL;
985         }
986
987         /*
988          * Create a fake SMB request that we will never send out. This is only
989          * used to be set into the pending queue with the right mid.
990          */
991         subreq = cli_smb_req_create(mem_ctx, ev, cli, 0, 0, 0, NULL, 0, NULL);
992         if (tevent_req_nomem(subreq, req)) {
993                 return tevent_req_post(req, ev);
994         }
995         smb_state = tevent_req_data(subreq, struct cli_smb_state);
996         SSVAL(smb_state->header, smb_mid, 0xffff);
997
998         if (!cli_smb_req_set_pending(subreq)) {
999                 tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
1000                 return tevent_req_post(req, ev);
1001         }
1002         tevent_req_set_callback(subreq, cli_smb_oplock_break_waiter_done, req);
1003         return req;
1004 }
1005
1006 static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq)
1007 {
1008         struct tevent_req *req = tevent_req_callback_data(
1009                 subreq, struct tevent_req);
1010         struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
1011                 req, struct cli_smb_oplock_break_waiter_state);
1012         uint8_t wct;
1013         uint16_t *vwv;
1014         uint32_t num_bytes;
1015         uint8_t *bytes;
1016         uint8_t *inbuf;
1017         NTSTATUS status;
1018
1019         status = cli_smb_recv(subreq, state, &inbuf, 8, &wct, &vwv,
1020                               &num_bytes, &bytes);
1021         TALLOC_FREE(subreq);
1022         if (!NT_STATUS_IS_OK(status)) {
1023                 tevent_req_nterror(req, status);
1024                 return;
1025         }
1026         state->fnum = SVAL(vwv+2, 0);
1027         state->level = CVAL(vwv+3, 1);
1028         tevent_req_done(req);
1029 }
1030
1031 NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req,
1032                                           uint16_t *pfnum,
1033                                           uint8_t *plevel)
1034 {
1035         struct cli_smb_oplock_break_waiter_state *state = tevent_req_data(
1036                 req, struct cli_smb_oplock_break_waiter_state);
1037         NTSTATUS status;
1038
1039         if (tevent_req_is_nterror(req, &status)) {
1040                 return status;
1041         }
1042         *pfnum = state->fnum;
1043         *plevel = state->level;
1044         return NT_STATUS_OK;
1045 }
1046
1047
1048 struct cli_session_request_state {
1049         struct tevent_context *ev;
1050         int sock;
1051         uint32 len_hdr;
1052         struct iovec iov[3];
1053         uint8_t nb_session_response;
1054 };
1055
1056 static void cli_session_request_sent(struct tevent_req *subreq);
1057 static void cli_session_request_recvd(struct tevent_req *subreq);
1058
1059 struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx,
1060                                             struct tevent_context *ev,
1061                                             int sock,
1062                                             const struct nmb_name *called,
1063                                             const struct nmb_name *calling)
1064 {
1065         struct tevent_req *req, *subreq;
1066         struct cli_session_request_state *state;
1067
1068         req = tevent_req_create(mem_ctx, &state,
1069                                 struct cli_session_request_state);
1070         if (req == NULL) {
1071                 return NULL;
1072         }
1073         state->ev = ev;
1074         state->sock = sock;
1075
1076         state->iov[1].iov_base = name_mangle(
1077                 state, called->name, called->name_type);
1078         if (tevent_req_nomem(state->iov[1].iov_base, req)) {
1079                 return tevent_req_post(req, ev);
1080         }
1081         state->iov[1].iov_len = name_len(
1082                 (unsigned char *)state->iov[1].iov_base,
1083                 talloc_get_size(state->iov[1].iov_base));
1084
1085         state->iov[2].iov_base = name_mangle(
1086                 state, calling->name, calling->name_type);
1087         if (tevent_req_nomem(state->iov[2].iov_base, req)) {
1088                 return tevent_req_post(req, ev);
1089         }
1090         state->iov[2].iov_len = name_len(
1091                 (unsigned char *)state->iov[2].iov_base,
1092                 talloc_get_size(state->iov[2].iov_base));
1093
1094         _smb_setlen(((char *)&state->len_hdr),
1095                     state->iov[1].iov_len + state->iov[2].iov_len);
1096         SCVAL((char *)&state->len_hdr, 0, 0x81);
1097
1098         state->iov[0].iov_base = &state->len_hdr;
1099         state->iov[0].iov_len = sizeof(state->len_hdr);
1100
1101         subreq = writev_send(state, ev, NULL, sock, true, state->iov, 3);
1102         if (tevent_req_nomem(subreq, req)) {
1103                 return tevent_req_post(req, ev);
1104         }
1105         tevent_req_set_callback(subreq, cli_session_request_sent, req);
1106         return req;
1107 }
1108
1109 static void cli_session_request_sent(struct tevent_req *subreq)
1110 {
1111         struct tevent_req *req = tevent_req_callback_data(
1112                 subreq, struct tevent_req);
1113         struct cli_session_request_state *state = tevent_req_data(
1114                 req, struct cli_session_request_state);
1115         ssize_t ret;
1116         int err;
1117
1118         ret = writev_recv(subreq, &err);
1119         TALLOC_FREE(subreq);
1120         if (ret == -1) {
1121                 tevent_req_error(req, err);
1122                 return;
1123         }
1124         subreq = read_smb_send(state, state->ev, state->sock);
1125         if (tevent_req_nomem(subreq, req)) {
1126                 return;
1127         }
1128         tevent_req_set_callback(subreq, cli_session_request_recvd, req);
1129 }
1130
1131 static void cli_session_request_recvd(struct tevent_req *subreq)
1132 {
1133         struct tevent_req *req = tevent_req_callback_data(
1134                 subreq, struct tevent_req);
1135         struct cli_session_request_state *state = tevent_req_data(
1136                 req, struct cli_session_request_state);
1137         uint8_t *buf;
1138         ssize_t ret;
1139         int err;
1140
1141         ret = read_smb_recv(subreq, talloc_tos(), &buf, &err);
1142         TALLOC_FREE(subreq);
1143
1144         if (ret < 4) {
1145                 ret = -1;
1146                 err = EIO;
1147         }
1148         if (ret == -1) {
1149                 tevent_req_error(req, err);
1150                 return;
1151         }
1152         /*
1153          * In case of an error there is more information in the data
1154          * portion according to RFC1002. We're not subtle enough to
1155          * respond to the different error conditions, so drop the
1156          * error info here.
1157          */
1158         state->nb_session_response = CVAL(buf, 0);
1159         tevent_req_done(req);
1160 }
1161
1162 bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp)
1163 {
1164         struct cli_session_request_state *state = tevent_req_data(
1165                 req, struct cli_session_request_state);
1166
1167         if (tevent_req_is_unix_error(req, err)) {
1168                 return false;
1169         }
1170         *resp = state->nb_session_response;
1171         return true;
1172 }