2 Unix SMB/CIFS implementation.
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/>.
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
;
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
=
50 struct smb2cli_req_state
);
51 struct cli_state
*cli
= state
->cli
;
52 int num_pending
= talloc_array_length(cli
->conn
.pending
);
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
);
67 for (i
=0; i
<num_pending
; i
++) {
68 if (req
== cli
->conn
.pending
[i
]) {
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.
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
,
98 static int smb2cli_req_destructor(struct tevent_req
*req
)
100 smb2cli_req_unset_pending(req
);
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
=
110 struct smb2cli_req_state
);
111 struct cli_state
*cli
;
112 struct tevent_req
**pending
;
114 struct tevent_req
*subreq
;
117 num_pending
= talloc_array_length(cli
->conn
.pending
);
119 pending
= talloc_realloc(cli
, cli
->conn
.pending
, struct tevent_req
*,
121 if (pending
== NULL
) {
124 pending
[num_pending
] = req
;
125 cli
->conn
.pending
= pending
;
126 talloc_set_destructor(req
, smb2cli_req_destructor
);
128 if (num_pending
> 0) {
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
);
141 tevent_req_set_callback(subreq
, smb2cli_inbuf_received
, cli
);
145 static void smb2cli_notify_pending(struct cli_state
*cli
, NTSTATUS status
)
147 if (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
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
,
179 uint32_t additional_flags
,
180 uint32_t clear_flags
,
181 unsigned int timeout
,
185 const uint8_t *fixed
,
190 struct tevent_req
*req
;
191 struct smb2cli_req_state
*state
;
194 req
= tevent_req_create(mem_ctx
, &state
,
195 struct smb2cli_req_state
);
202 state
->recv_iov
= talloc_zero_array(state
, struct iovec
, 3);
203 if (state
->recv_iov
== NULL
) {
208 flags
|= additional_flags
;
209 flags
&= ~clear_flags
;
211 state
->fixed
= fixed
;
212 state
->fixed_len
= fixed_len
;
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
);
228 struct timeval endtime
;
230 endtime
= timeval_current_ofs_msec(timeout
);
231 if (!tevent_req_set_endtime(req
, ev
, endtime
)) {
239 static void smb2cli_writev_done(struct tevent_req
*subreq
);
241 NTSTATUS
smb2cli_req_compound_submit(struct tevent_req
**reqs
,
244 struct smb2cli_req_state
*state
;
245 struct tevent_req
*subreq
;
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);
257 return NT_STATUS_NO_MEMORY
;
263 for (i
=0; i
<num_reqs
; i
++) {
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
);
291 iov
[num_iov
].iov_base
= discard_const(state
->fixed
);
292 iov
[num_iov
].iov_len
= state
->fixed_len
;
295 if (state
->dyn
!= NULL
) {
296 iov
[num_iov
].iov_base
= discard_const(state
->dyn
);
297 iov
[num_iov
].iov_len
= state
->dyn_len
;
301 reqlen
= sizeof(state
->hdr
) + state
->fixed_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
;
312 SIVAL(state
->hdr
, SMB2_HDR_NEXT_COMMAND
, reqlen
);
316 ret
= smb2cli_req_set_pending(reqs
[i
]);
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]);
340 struct tevent_req
*smb2cli_req_send(TALLOC_CTX
*mem_ctx
,
341 struct tevent_context
*ev
,
342 struct cli_state
*cli
,
344 uint32_t additional_flags
,
345 uint32_t clear_flags
,
346 unsigned int timeout
,
350 const uint8_t *fixed
,
355 struct tevent_req
*req
;
358 req
= smb2cli_req_create(mem_ctx
, ev
, cli
, cmd
,
359 additional_flags
, clear_flags
,
362 fixed
, fixed_len
, dyn
, dyn_len
);
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
);
376 static void smb2cli_writev_done(struct tevent_req
*subreq
)
378 struct tevent_req
*req
=
379 tevent_req_callback_data(subreq
,
381 struct smb2cli_req_state
*state
=
383 struct smb2cli_req_state
);
387 nwritten
= writev_recv(subreq
, &err
);
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
);
397 static NTSTATUS
smb2cli_inbuf_parse_compound(uint8_t *buf
, TALLOC_CTX
*mem_ctx
,
398 struct iovec
**piov
, int *pnum_iov
)
407 iov
= talloc_array(mem_ctx
, struct iovec
, num_iov
);
409 return NT_STATUS_NO_MEMORY
;
411 iov
[0].iov_base
= buf
;
414 buflen
= smb_len_tcp(buf
) + 4;
417 while (taken
< buflen
) {
418 size_t len
= buflen
- taken
;
419 uint8_t *hdr
= buf
+ taken
;
422 size_t next_command_ofs
;
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
));
435 if (IVAL(hdr
, 0) != SMB2_MAGIC
) {
436 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
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
));
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)) {
454 if (next_command_ofs
> full_size
) {
457 full_size
= next_command_ofs
;
464 if (body_size
> (full_size
- SMB2_HDR_BODY
)) {
468 iov_tmp
= talloc_realloc(mem_ctx
, iov
, struct iovec
,
470 if (iov_tmp
== NULL
) {
472 return NT_STATUS_NO_MEMORY
;
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
);
494 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
497 static struct tevent_req
*cli_smb2_find_pending(struct cli_state
*cli
,
500 int num_pending
= talloc_array_length(cli
->conn
.pending
);
503 for (i
=0; i
<num_pending
; i
++) {
504 struct tevent_req
*req
= cli
->conn
.pending
[i
];
505 struct smb2cli_req_state
*state
=
507 struct smb2cli_req_state
);
509 if (mid
== BVAL(state
->hdr
, SMB2_HDR_MESSAGE_ID
)) {
516 static void smb2cli_inbuf_received(struct tevent_req
*subreq
)
518 struct cli_state
*cli
=
519 tevent_req_callback_data(subreq
,
521 TALLOC_CTX
*frame
= talloc_stackframe();
522 struct tevent_req
*req
;
523 struct smb2cli_req_state
*state
= NULL
;
533 received
= read_smb_recv(subreq
, frame
, &inbuf
, &err
);
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
);
546 status
= smb2cli_inbuf_parse_compound(inbuf
, frame
,
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
);
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
);
570 req
= cli_smb2_find_pending(cli
, mid
);
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
);
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
);
595 if (!(flags
& SMB2_HDR_FLAG_REDIRECT
)) {
596 status
= NT_STATUS_INVALID_NETWORK_RESPONSE
;
597 smb2cli_notify_pending(cli
, status
);
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
);
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)) {
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
)) {
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
);
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 */
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
);
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
);
678 tevent_req_set_callback(subreq
, smb2cli_inbuf_received
, cli
);
681 NTSTATUS
smb2cli_req_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
683 const struct smb2cli_req_expected_response
*expected
,
686 struct smb2cli_req_state
*state
=
688 struct smb2cli_req_state
);
691 bool found_status
= false;
692 bool found_size
= false;
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
)) {
708 return NT_STATUS_UNEXPECTED_NETWORK_ERROR
;
714 if (num_expected
== 0) {
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
)) {
728 if (expected
[i
].body_size
== 0) {
733 if (expected
[i
].body_size
== body_size
) {
744 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
748 *piov
= talloc_move(mem_ctx
, &state
->recv_iov
);