s3:smb2cli_base: make use of [_]smb_[set]len_tcp()
[Samba/gebeck_regimport.git] / source3 / libsmb / smb2cli_base.c
blobde4a06bd2d2dba71bb0622d85510e1f9cc37a658
1 /*
2 Unix SMB/CIFS implementation.
3 smb2 lib
4 Copyright (C) Volker Lendecke 2011
5 Copyright (C) Stefan Metzmacher 2011
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.
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.
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/>.
21 #include "includes.h"
22 #include "client.h"
23 #include "libcli/smb/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"
29 struct smb2cli_req_state {
30 struct tevent_context *ev;
31 struct cli_state *cli;
33 const uint8_t *fixed;
34 uint16_t fixed_len;
35 const uint8_t *dyn;
36 uint32_t dyn_len;
38 uint8_t nbt[4];
39 uint8_t hdr[64];
40 uint8_t pad[7]; /* padding space for compounding */
42 /* always an array of 3 talloc elements */
43 struct iovec *recv_iov;
46 static void smb2cli_req_unset_pending(struct tevent_req *req)
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;
55 talloc_set_destructor(req, NULL);
57 if (num_pending == 1) {
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.
63 TALLOC_FREE(cli->conn.pending);
64 return;
67 for (i=0; i<num_pending; i++) {
68 if (req == cli->conn.pending[i]) {
69 break;
72 if (i == num_pending) {
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.
78 return;
82 * Remove ourselves from the cli->pending array
84 for (; i < (num_pending - 1); i++) {
85 cli->conn.pending[i] = cli->conn.pending[i+1];
89 * No NULL check here, we're shrinking by sizeof(void *), and
90 * talloc_realloc just adjusts the size for this.
92 cli->conn.pending = talloc_realloc(NULL, cli->conn.pending,
93 struct tevent_req *,
94 num_pending - 1);
95 return;
98 static int smb2cli_req_destructor(struct tevent_req *req)
100 smb2cli_req_unset_pending(req);
101 return 0;
104 static void smb2cli_inbuf_received(struct tevent_req *subreq);
106 static bool smb2cli_req_set_pending(struct tevent_req *req)
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;
116 cli = state->cli;
117 num_pending = talloc_array_length(cli->conn.pending);
119 pending = talloc_realloc(cli, cli->conn.pending, struct tevent_req *,
120 num_pending+1);
121 if (pending == NULL) {
122 return false;
124 pending[num_pending] = req;
125 cli->conn.pending = pending;
126 talloc_set_destructor(req, smb2cli_req_destructor);
128 if (num_pending > 0) {
129 return true;
133 * We're the first ones, add the read_smb request that waits for the
134 * answer from the server
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;
141 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
142 return true;
145 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
147 if (cli->conn.fd != -1) {
148 close(cli->conn.fd);
150 cli->conn.fd = -1;
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().
157 while (talloc_array_length(cli->conn.pending) > 0) {
158 struct tevent_req *req;
159 struct smb2cli_req_state *state;
161 req = cli->conn.pending[0];
162 state = tevent_req_data(req, struct smb2cli_req_state);
164 smb2cli_req_unset_pending(req);
167 * we need to defer the callback, because we may notify more
168 * then one caller.
170 tevent_req_defer_callback(req, state->ev);
171 tevent_req_nterror(req, status);
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 unsigned int timeout,
182 uint32_t pid,
183 uint32_t tid,
184 uint64_t uid,
185 const uint8_t *fixed,
186 uint16_t fixed_len,
187 const uint8_t *dyn,
188 uint32_t dyn_len)
190 struct tevent_req *req;
191 struct smb2cli_req_state *state;
192 uint32_t flags = 0;
194 req = tevent_req_create(mem_ctx, &state,
195 struct smb2cli_req_state);
196 if (req == NULL) {
197 return NULL;
199 state->ev = ev;
200 state->cli = cli;
202 state->recv_iov = talloc_zero_array(state, struct iovec, 3);
203 if (state->recv_iov == NULL) {
204 TALLOC_FREE(req);
205 return NULL;
208 flags |= additional_flags;
209 flags &= ~clear_flags;
211 state->fixed = fixed;
212 state->fixed_len = fixed_len;
213 state->dyn = dyn;
214 state->dyn_len = dyn_len;
216 SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
217 SSVAL(state->hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
218 SSVAL(state->hdr, SMB2_HDR_EPOCH, 1);
219 SIVAL(state->hdr, SMB2_HDR_STATUS, NT_STATUS_V(NT_STATUS_OK));
220 SSVAL(state->hdr, SMB2_HDR_OPCODE, cmd);
221 SSVAL(state->hdr, SMB2_HDR_CREDIT, 31);
222 SIVAL(state->hdr, SMB2_HDR_FLAGS, flags);
223 SIVAL(state->hdr, SMB2_HDR_PID, pid);
224 SIVAL(state->hdr, SMB2_HDR_TID, tid);
225 SBVAL(state->hdr, SMB2_HDR_SESSION_ID, uid);
227 if (timeout > 0) {
228 struct timeval endtime;
230 endtime = timeval_current_ofs_msec(timeout);
231 if (!tevent_req_set_endtime(req, ev, endtime)) {
232 return req;
236 return req;
239 static void smb2cli_writev_done(struct tevent_req *subreq);
241 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
242 int num_reqs)
244 struct smb2cli_req_state *state;
245 struct tevent_req *subreq;
246 struct iovec *iov;
247 int i, num_iov, nbt_len;
250 * 1 for the nbt length
251 * per request: HDR, fixed, dyn, padding
252 * -1 because the last one does not need padding
255 iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
256 if (iov == NULL) {
257 return NT_STATUS_NO_MEMORY;
260 num_iov = 1;
261 nbt_len = 0;
263 for (i=0; i<num_reqs; i++) {
264 size_t reqlen;
265 bool ret;
266 uint64_t mid;
268 if (!tevent_req_is_in_progress(reqs[i])) {
269 return NT_STATUS_INTERNAL_ERROR;
272 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
274 if (!cli_state_is_connected(state->cli)) {
275 return NT_STATUS_CONNECTION_DISCONNECTED;
278 if (state->cli->smb2.mid == UINT64_MAX) {
279 return NT_STATUS_CONNECTION_ABORTED;
282 mid = state->cli->smb2.mid;
283 state->cli->smb2.mid += 1;
285 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, mid);
287 iov[num_iov].iov_base = state->hdr;
288 iov[num_iov].iov_len = sizeof(state->hdr);
289 num_iov += 1;
291 iov[num_iov].iov_base = discard_const(state->fixed);
292 iov[num_iov].iov_len = state->fixed_len;
293 num_iov += 1;
295 if (state->dyn != NULL) {
296 iov[num_iov].iov_base = discard_const(state->dyn);
297 iov[num_iov].iov_len = state->dyn_len;
298 num_iov += 1;
301 reqlen = sizeof(state->hdr) + state->fixed_len +
302 state->dyn_len;
304 if (i < num_reqs-1) {
305 if ((reqlen % 8) > 0) {
306 uint8_t pad = 8 - (reqlen % 8);
307 iov[num_iov].iov_base = state->pad;
308 iov[num_iov].iov_len = pad;
309 num_iov += 1;
310 reqlen += pad;
312 SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
314 nbt_len += reqlen;
316 ret = smb2cli_req_set_pending(reqs[i]);
317 if (!ret) {
318 return NT_STATUS_NO_MEMORY;
323 * TODO: Do signing here
326 state = tevent_req_data(reqs[0], struct smb2cli_req_state);
327 _smb_setlen_tcp(state->nbt, nbt_len);
328 iov[0].iov_base = state->nbt;
329 iov[0].iov_len = sizeof(state->nbt);
331 subreq = writev_send(state, state->ev, state->cli->conn.outgoing,
332 state->cli->conn.fd, false, iov, num_iov);
333 if (subreq == NULL) {
334 return NT_STATUS_NO_MEMORY;
336 tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
337 return NT_STATUS_OK;
340 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
341 struct tevent_context *ev,
342 struct cli_state *cli,
343 uint16_t cmd,
344 uint32_t additional_flags,
345 uint32_t clear_flags,
346 unsigned int timeout,
347 uint32_t pid,
348 uint32_t tid,
349 uint64_t uid,
350 const uint8_t *fixed,
351 uint16_t fixed_len,
352 const uint8_t *dyn,
353 uint32_t dyn_len)
355 struct tevent_req *req;
356 NTSTATUS status;
358 req = smb2cli_req_create(mem_ctx, ev, cli, cmd,
359 additional_flags, clear_flags,
360 timeout,
361 pid, tid, uid,
362 fixed, fixed_len, dyn, dyn_len);
363 if (req == NULL) {
364 return NULL;
366 if (!tevent_req_is_in_progress(req)) {
367 return tevent_req_post(req, ev);
369 status = smb2cli_req_compound_submit(&req, 1);
370 if (tevent_req_nterror(req, status)) {
371 return tevent_req_post(req, ev);
373 return req;
376 static void smb2cli_writev_done(struct tevent_req *subreq)
378 struct tevent_req *req =
379 tevent_req_callback_data(subreq,
380 struct tevent_req);
381 struct smb2cli_req_state *state =
382 tevent_req_data(req,
383 struct smb2cli_req_state);
384 ssize_t nwritten;
385 int err;
387 nwritten = writev_recv(subreq, &err);
388 TALLOC_FREE(subreq);
389 if (nwritten == -1) {
390 /* here, we need to notify all pending requests */
391 NTSTATUS status = map_nt_error_from_unix_common(err);
392 smb2cli_notify_pending(state->cli, status);
393 return;
397 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
398 struct iovec **piov, int *pnum_iov)
400 struct iovec *iov;
401 int num_iov;
402 size_t buflen;
403 size_t taken;
405 num_iov = 1;
407 iov = talloc_array(mem_ctx, struct iovec, num_iov);
408 if (iov == NULL) {
409 return NT_STATUS_NO_MEMORY;
411 iov[0].iov_base = buf;
412 iov[0].iov_len = 4;
414 buflen = smb_len_tcp(buf) + 4;
415 taken = 4;
417 while (taken < buflen) {
418 size_t len = buflen - taken;
419 uint8_t *hdr = buf + taken;
420 struct iovec *cur;
421 size_t full_size;
422 size_t next_command_ofs;
423 uint16_t body_size;
424 struct iovec *iov_tmp;
427 * We need the header plus the body length field
430 if (len < SMB2_HDR_BODY + 2) {
431 DEBUG(10, ("%d bytes left, expected at least %d\n",
432 (int)len, SMB2_HDR_BODY));
433 goto inval;
435 if (IVAL(hdr, 0) != SMB2_MAGIC) {
436 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
437 IVAL(hdr, 0)));
438 goto inval;
440 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
441 DEBUG(10, ("Got HDR len %d, expected %d\n",
442 SVAL(hdr, 4), SMB2_HDR_BODY));
443 goto inval;
446 full_size = len;
447 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
448 body_size = SVAL(hdr, SMB2_HDR_BODY);
450 if (next_command_ofs != 0) {
451 if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
452 goto inval;
454 if (next_command_ofs > full_size) {
455 goto inval;
457 full_size = next_command_ofs;
459 if (body_size < 2) {
460 goto inval;
462 body_size &= 0xfffe;
464 if (body_size > (full_size - SMB2_HDR_BODY)) {
465 goto inval;
468 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
469 num_iov + 3);
470 if (iov_tmp == NULL) {
471 TALLOC_FREE(iov);
472 return NT_STATUS_NO_MEMORY;
474 iov = iov_tmp;
475 cur = &iov[num_iov];
476 num_iov += 3;
478 cur[0].iov_base = hdr;
479 cur[0].iov_len = SMB2_HDR_BODY;
480 cur[1].iov_base = hdr + SMB2_HDR_BODY;
481 cur[1].iov_len = body_size;
482 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
483 cur[2].iov_len = full_size - (SMB2_HDR_BODY + body_size);
485 taken += full_size;
488 *piov = iov;
489 *pnum_iov = num_iov;
490 return NT_STATUS_OK;
492 inval:
493 TALLOC_FREE(iov);
494 return NT_STATUS_INVALID_NETWORK_RESPONSE;
497 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
498 uint64_t mid)
500 int num_pending = talloc_array_length(cli->conn.pending);
501 int i;
503 for (i=0; i<num_pending; i++) {
504 struct tevent_req *req = cli->conn.pending[i];
505 struct smb2cli_req_state *state =
506 tevent_req_data(req,
507 struct smb2cli_req_state);
509 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
510 return req;
513 return NULL;
516 static void smb2cli_inbuf_received(struct tevent_req *subreq)
518 struct cli_state *cli =
519 tevent_req_callback_data(subreq,
520 struct cli_state);
521 TALLOC_CTX *frame = talloc_stackframe();
522 struct tevent_req *req;
523 struct smb2cli_req_state *state = NULL;
524 struct iovec *iov;
525 int i, num_iov;
526 NTSTATUS status;
527 uint8_t *inbuf;
528 ssize_t received;
529 int err;
530 size_t num_pending;
531 bool defer = true;
533 received = read_smb_recv(subreq, frame, &inbuf, &err);
534 TALLOC_FREE(subreq);
535 if (received == -1) {
537 * We need to close the connection and notify
538 * all pending requests.
540 status = map_nt_error_from_unix_common(err);
541 smb2cli_notify_pending(cli, status);
542 TALLOC_FREE(frame);
543 return;
546 status = smb2cli_inbuf_parse_compound(inbuf, frame,
547 &iov, &num_iov);
548 if (!NT_STATUS_IS_OK(status)) {
550 * if we cannot parse the incoming pdu,
551 * the connection becomes unusable.
553 * We need to close the connection and notify
554 * all pending requests.
556 smb2cli_notify_pending(cli, status);
557 TALLOC_FREE(frame);
558 return;
561 for (i=1; i<num_iov; i+=3) {
562 uint8_t *inbuf_ref = NULL;
563 struct iovec *cur = &iov[i];
564 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
565 uint16_t opcode = SVAL(inhdr, SMB2_HDR_OPCODE);
566 uint32_t flags = IVAL(inhdr, SMB2_HDR_FLAGS);
567 uint64_t mid = BVAL(inhdr, SMB2_HDR_MESSAGE_ID);
568 uint16_t req_opcode;
570 req = cli_smb2_find_pending(cli, mid);
571 if (req == NULL) {
573 * TODO: handle oplock breaks and async responses
577 * We need to close the connection and notify
578 * all pending requests.
580 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
581 smb2cli_notify_pending(cli, status);
582 TALLOC_FREE(frame);
583 return;
585 state = tevent_req_data(req, struct smb2cli_req_state);
587 req_opcode = SVAL(state->hdr, SMB2_HDR_OPCODE);
588 if (opcode != req_opcode) {
589 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
590 smb2cli_notify_pending(cli, status);
591 TALLOC_FREE(frame);
592 return;
595 if (!(flags & SMB2_HDR_FLAG_REDIRECT)) {
596 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
597 smb2cli_notify_pending(cli, status);
598 TALLOC_FREE(frame);
599 return;
602 status = NT_STATUS(IVAL(inhdr, SMB2_HDR_STATUS));
603 if ((flags & SMB2_HDR_FLAG_ASYNC) &&
604 NT_STATUS_EQUAL(status, STATUS_PENDING)) {
605 uint32_t req_flags = IVAL(state->hdr, SMB2_HDR_FLAGS);
606 uint64_t async_id = BVAL(inhdr, SMB2_HDR_ASYNC_ID);
608 req_flags |= SMB2_HDR_FLAG_ASYNC;
609 SBVAL(state->hdr, SMB2_HDR_FLAGS, req_flags);
610 SBVAL(state->hdr, SMB2_HDR_ASYNC_ID, async_id);
611 continue;
614 smb2cli_req_unset_pending(req);
617 * There might be more than one response
618 * we need to defer the notifications
620 if ((num_iov == 4) && (talloc_array_length(cli->conn.pending) == 0)) {
621 defer = false;
624 if (defer) {
625 tevent_req_defer_callback(req, state->ev);
629 * Note: here we use talloc_reference() in a way
630 * that does not expose it to the caller.
632 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
633 if (tevent_req_nomem(inbuf_ref, req)) {
634 continue;
637 /* copy the related buffers */
638 state->recv_iov[0] = cur[0];
639 state->recv_iov[1] = cur[1];
640 state->recv_iov[2] = cur[2];
642 tevent_req_done(req);
645 TALLOC_FREE(frame);
647 if (!defer) {
648 return;
651 num_pending = talloc_array_length(cli->conn.pending);
652 if (num_pending == 0) {
653 if (state->cli->smb2.mid < UINT64_MAX) {
654 /* no more pending requests, so we are done for now */
655 return;
659 * If there are no more requests possible,
660 * because we are out of message ids,
661 * we need to disconnect.
663 smb2cli_notify_pending(cli, NT_STATUS_CONNECTION_ABORTED);
664 return;
666 req = cli->conn.pending[0];
667 state = tevent_req_data(req, struct smb2cli_req_state);
670 * add the read_smb request that waits for the
671 * next answer from the server
673 subreq = read_smb_send(cli->conn.pending, state->ev, cli->conn.fd);
674 if (subreq == NULL) {
675 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
676 return;
678 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
681 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
682 struct iovec **piov,
683 const struct smb2cli_req_expected_response *expected,
684 size_t num_expected)
686 struct smb2cli_req_state *state =
687 tevent_req_data(req,
688 struct smb2cli_req_state);
689 NTSTATUS status;
690 size_t body_size;
691 bool found_status = false;
692 bool found_size = false;
693 size_t i;
695 if (piov != NULL) {
696 *piov = NULL;
699 if (tevent_req_is_nterror(req, &status)) {
700 for (i=0; i < num_expected; i++) {
701 if (NT_STATUS_EQUAL(status, expected[i].status)) {
702 found_status = true;
703 break;
707 if (found_status) {
708 return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
711 return status;
714 if (num_expected == 0) {
715 found_status = true;
716 found_size = true;
719 status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
720 body_size = SVAL(state->recv_iov[1].iov_base, 0);
722 for (i=0; i < num_expected; i++) {
723 if (!NT_STATUS_EQUAL(status, expected[i].status)) {
724 continue;
727 found_status = true;
728 if (expected[i].body_size == 0) {
729 found_size = true;
730 break;
733 if (expected[i].body_size == body_size) {
734 found_size = true;
735 break;
739 if (!found_status) {
740 return status;
743 if (!found_size) {
744 return NT_STATUS_INVALID_NETWORK_RESPONSE;
747 if (piov != NULL) {
748 *piov = talloc_move(mem_ctx, &state->recv_iov);
751 return status;