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/>.
24 #include "smb2cli_base.h"
25 #include "lib/async_req/async_sock.h"
26 #include "lib/util/tevent_ntstatus.h"
28 struct smb2cli_req_state
{
29 struct tevent_context
*ev
;
30 struct cli_state
*cli
;
39 uint8_t pad
[7]; /* padding space for compounding */
41 /* always an array of 3 talloc elements */
42 struct iovec
*recv_iov
;
45 static void smb2cli_req_unset_pending(struct tevent_req
*req
)
47 struct smb2cli_req_state
*state
=
49 struct smb2cli_req_state
);
50 struct cli_state
*cli
= state
->cli
;
51 int num_pending
= talloc_array_length(cli
->conn
.pending
);
54 talloc_set_destructor(req
, NULL
);
56 if (num_pending
== 1) {
58 * The pending read_smb tevent_req is a child of
59 * cli->conn.pending. So if nothing is pending anymore,
60 * we need to delete the socket read fde.
62 TALLOC_FREE(cli
->conn
.pending
);
66 for (i
=0; i
<num_pending
; i
++) {
67 if (req
== cli
->conn
.pending
[i
]) {
71 if (i
== num_pending
) {
73 * Something's seriously broken. Just returning here is the
74 * right thing nevertheless, the point of this routine is to
75 * remove ourselves from cli->conn.pending.
81 * Remove ourselves from the cli->pending array
83 for (; i
< (num_pending
- 1); i
++) {
84 cli
->conn
.pending
[i
] = cli
->conn
.pending
[i
+1];
88 * No NULL check here, we're shrinking by sizeof(void *), and
89 * talloc_realloc just adjusts the size for this.
91 cli
->conn
.pending
= talloc_realloc(NULL
, cli
->conn
.pending
,
97 static int smb2cli_req_destructor(struct tevent_req
*req
)
99 smb2cli_req_unset_pending(req
);
103 static void smb2cli_inbuf_received(struct tevent_req
*subreq
);
105 static bool smb2cli_req_set_pending(struct tevent_req
*req
)
107 struct smb2cli_req_state
*state
=
109 struct smb2cli_req_state
);
110 struct cli_state
*cli
;
111 struct tevent_req
**pending
;
113 struct tevent_req
*subreq
;
116 num_pending
= talloc_array_length(cli
->conn
.pending
);
118 pending
= talloc_realloc(cli
, cli
->conn
.pending
, struct tevent_req
*,
120 if (pending
== NULL
) {
123 pending
[num_pending
] = req
;
124 cli
->conn
.pending
= pending
;
125 talloc_set_destructor(req
, smb2cli_req_destructor
);
127 if (num_pending
> 0) {
132 * We're the first ones, add the read_smb request that waits for the
133 * answer from the server
135 subreq
= read_smb_send(cli
->conn
.pending
, state
->ev
, cli
->fd
);
136 if (subreq
== NULL
) {
137 smb2cli_req_unset_pending(req
);
140 tevent_req_set_callback(subreq
, smb2cli_inbuf_received
, cli
);
144 static void smb2cli_notify_pending(struct cli_state
*cli
, NTSTATUS status
)
152 * Cancel all pending requests. We don't do a for-loop walking
153 * cli->conn.pending because that array changes in
154 * cli_smb_req_destructor().
156 while (talloc_array_length(cli
->conn
.pending
) > 0) {
157 struct tevent_req
*req
;
158 struct smb2cli_req_state
*state
;
160 req
= cli
->conn
.pending
[0];
161 state
= tevent_req_data(req
, struct smb2cli_req_state
);
163 smb2cli_req_unset_pending(req
);
166 * we need to defer the callback, because we may notify more
169 tevent_req_defer_callback(req
, state
->ev
);
170 tevent_req_nterror(req
, status
);
174 struct tevent_req
*smb2cli_req_create(TALLOC_CTX
*mem_ctx
,
175 struct tevent_context
*ev
,
176 struct cli_state
*cli
,
179 const uint8_t *fixed
,
184 struct tevent_req
*req
;
185 struct smb2cli_req_state
*state
;
187 req
= tevent_req_create(mem_ctx
, &state
,
188 struct smb2cli_req_state
);
195 state
->recv_iov
= talloc_zero_array(state
, struct iovec
, 3);
196 if (state
->recv_iov
== NULL
) {
201 state
->fixed
= fixed
;
202 state
->fixed_len
= fixed_len
;
204 state
->dyn_len
= dyn_len
;
206 SIVAL(state
->hdr
, SMB2_HDR_PROTOCOL_ID
, SMB2_MAGIC
);
207 SSVAL(state
->hdr
, SMB2_HDR_LENGTH
, SMB2_HDR_BODY
);
208 SSVAL(state
->hdr
, SMB2_HDR_EPOCH
, 1);
209 SIVAL(state
->hdr
, SMB2_HDR_STATUS
, NT_STATUS_V(NT_STATUS_OK
));
210 SSVAL(state
->hdr
, SMB2_HDR_OPCODE
, cmd
);
211 SSVAL(state
->hdr
, SMB2_HDR_CREDIT
, 31);
212 SIVAL(state
->hdr
, SMB2_HDR_FLAGS
, flags
);
213 SIVAL(state
->hdr
, SMB2_HDR_PID
, cli
->smb2
.pid
);
214 SIVAL(state
->hdr
, SMB2_HDR_TID
, cli
->smb2
.tid
);
215 SBVAL(state
->hdr
, SMB2_HDR_SESSION_ID
, cli
->smb2
.uid
);
220 static void smb2cli_writev_done(struct tevent_req
*subreq
);
222 NTSTATUS
smb2cli_req_compound_submit(struct tevent_req
**reqs
,
225 struct smb2cli_req_state
*state
;
226 struct tevent_req
*subreq
;
228 int i
, num_iov
, nbt_len
;
231 * 1 for the nbt length
232 * per request: HDR, fixed, dyn, padding
233 * -1 because the last one does not need padding
236 iov
= talloc_array(reqs
[0], struct iovec
, 1 + 4*num_reqs
- 1);
238 return NT_STATUS_NO_MEMORY
;
244 for (i
=0; i
<num_reqs
; i
++) {
248 state
= tevent_req_data(reqs
[i
], struct smb2cli_req_state
);
250 SBVAL(state
->hdr
, SMB2_HDR_MESSAGE_ID
, state
->cli
->smb2
.mid
++);
252 iov
[num_iov
].iov_base
= state
->hdr
;
253 iov
[num_iov
].iov_len
= sizeof(state
->hdr
);
256 iov
[num_iov
].iov_base
= discard_const(state
->fixed
);
257 iov
[num_iov
].iov_len
= state
->fixed_len
;
260 if (state
->dyn
!= NULL
) {
261 iov
[num_iov
].iov_base
= discard_const(state
->dyn
);
262 iov
[num_iov
].iov_len
= state
->dyn_len
;
266 reqlen
= sizeof(state
->hdr
) + state
->fixed_len
+
269 if (i
< num_reqs
-1) {
270 if ((reqlen
% 8) > 0) {
271 uint8_t pad
= 8 - (reqlen
% 8);
272 iov
[num_iov
].iov_base
= state
->pad
;
273 iov
[num_iov
].iov_len
= pad
;
277 SIVAL(state
->hdr
, SMB2_HDR_NEXT_COMMAND
, reqlen
);
281 ret
= smb2cli_req_set_pending(reqs
[i
]);
283 return NT_STATUS_NO_MEMORY
;
288 * TODO: Do signing here
291 state
= tevent_req_data(reqs
[0], struct smb2cli_req_state
);
292 _smb_setlen_large(state
->nbt
, nbt_len
);
293 iov
[0].iov_base
= state
->nbt
;
294 iov
[0].iov_len
= sizeof(state
->nbt
);
296 subreq
= writev_send(state
, state
->ev
, state
->cli
->outgoing
,
297 state
->cli
->fd
, false, iov
, num_iov
);
298 if (subreq
== NULL
) {
299 return NT_STATUS_NO_MEMORY
;
301 tevent_req_set_callback(subreq
, smb2cli_writev_done
, reqs
[0]);
305 struct tevent_req
*smb2cli_req_send(TALLOC_CTX
*mem_ctx
,
306 struct tevent_context
*ev
,
307 struct cli_state
*cli
,
310 const uint8_t *fixed
,
315 struct tevent_req
*req
;
318 req
= smb2cli_req_create(mem_ctx
, ev
, cli
, cmd
, flags
,
319 fixed
, fixed_len
, dyn
, dyn_len
);
323 if (!tevent_req_is_in_progress(req
)) {
326 status
= smb2cli_req_compound_submit(&req
, 1);
327 if (tevent_req_nterror(req
, status
)) {
328 return tevent_req_post(req
, ev
);
333 static void smb2cli_writev_done(struct tevent_req
*subreq
)
335 struct tevent_req
*req
=
336 tevent_req_callback_data(subreq
,
338 struct smb2cli_req_state
*state
=
340 struct smb2cli_req_state
);
344 nwritten
= writev_recv(subreq
, &err
);
346 if (nwritten
== -1) {
347 /* here, we need to notify all pending requests */
348 smb2cli_notify_pending(state
->cli
, map_nt_error_from_unix(err
));
353 static NTSTATUS
smb2cli_inbuf_parse_compound(uint8_t *buf
, TALLOC_CTX
*mem_ctx
,
354 struct iovec
**piov
, int *pnum_iov
)
363 iov
= talloc_array(mem_ctx
, struct iovec
, num_iov
);
365 return NT_STATUS_NO_MEMORY
;
367 iov
[0].iov_base
= buf
;
370 buflen
= smb_len_large(buf
) + 4;
373 while (taken
< buflen
) {
374 size_t len
= buflen
- taken
;
375 uint8_t *hdr
= buf
+ taken
;
378 size_t next_command_ofs
;
380 struct iovec
*iov_tmp
;
383 * We need the header plus the body length field
386 if (len
< SMB2_HDR_BODY
+ 2) {
387 DEBUG(10, ("%d bytes left, expected at least %d\n",
388 (int)len
, SMB2_HDR_BODY
));
391 if (IVAL(hdr
, 0) != SMB2_MAGIC
) {
392 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
396 if (SVAL(hdr
, 4) != SMB2_HDR_BODY
) {
397 DEBUG(10, ("Got HDR len %d, expected %d\n",
398 SVAL(hdr
, 4), SMB2_HDR_BODY
));
403 next_command_ofs
= IVAL(hdr
, SMB2_HDR_NEXT_COMMAND
);
404 body_size
= SVAL(hdr
, SMB2_HDR_BODY
);
406 if (next_command_ofs
!= 0) {
407 if (next_command_ofs
< (SMB2_HDR_BODY
+ 2)) {
410 if (next_command_ofs
> full_size
) {
413 full_size
= next_command_ofs
;
420 if (body_size
> (full_size
- SMB2_HDR_BODY
)) {
424 iov_tmp
= talloc_realloc(mem_ctx
, iov
, struct iovec
,
426 if (iov_tmp
== NULL
) {
428 return NT_STATUS_NO_MEMORY
;
434 cur
[0].iov_base
= hdr
;
435 cur
[0].iov_len
= SMB2_HDR_BODY
;
436 cur
[1].iov_base
= hdr
+ SMB2_HDR_BODY
;
437 cur
[1].iov_len
= body_size
;
438 cur
[2].iov_base
= hdr
+ SMB2_HDR_BODY
+ body_size
;
439 cur
[2].iov_len
= full_size
- (SMB2_HDR_BODY
+ body_size
);
450 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
453 static struct tevent_req
*cli_smb2_find_pending(struct cli_state
*cli
,
456 int num_pending
= talloc_array_length(cli
->conn
.pending
);
459 for (i
=0; i
<num_pending
; i
++) {
460 struct tevent_req
*req
= cli
->conn
.pending
[i
];
461 struct smb2cli_req_state
*state
=
463 struct smb2cli_req_state
);
465 if (mid
== BVAL(state
->hdr
, SMB2_HDR_MESSAGE_ID
)) {
472 static void smb2cli_inbuf_received(struct tevent_req
*subreq
)
474 struct cli_state
*cli
=
475 tevent_req_callback_data(subreq
,
477 TALLOC_CTX
*frame
= talloc_stackframe();
478 struct tevent_req
*req
;
479 struct smb2cli_req_state
*state
;
488 received
= read_smb_recv(subreq
, frame
, &inbuf
, &err
);
490 if (received
== -1) {
492 * We need to close the connection and notify
493 * all pending requests.
495 smb2cli_notify_pending(cli
, map_nt_error_from_unix(err
));
500 status
= smb2cli_inbuf_parse_compound(inbuf
, frame
,
502 if (!NT_STATUS_IS_OK(status
)) {
504 * if we cannot parse the incoming pdu,
505 * the connection becomes unusable.
507 * We need to close the connection and notify
508 * all pending requests.
510 smb2cli_notify_pending(cli
, status
);
515 for (i
=1; i
<num_iov
; i
+=3) {
516 uint8_t *inbuf_ref
= NULL
;
517 struct iovec
*cur
= &iov
[i
];
518 uint8_t *inhdr
= (uint8_t *)cur
[0].iov_base
;
520 req
= cli_smb2_find_pending(
521 cli
, BVAL(inhdr
, SMB2_HDR_MESSAGE_ID
));
524 * TODO: handle oplock breaks and async responses
528 * We need to close the connection and notify
529 * all pending requests.
531 smb2cli_notify_pending(cli
, status
);
535 smb2cli_req_unset_pending(req
);
536 state
= tevent_req_data(req
, struct smb2cli_req_state
);
539 * There might be more than one response
540 * we need to defer the notifications
542 tevent_req_defer_callback(req
, state
->ev
);
545 * Note: here we use talloc_reference() in a way
546 * that does not expose it to the caller.
548 inbuf_ref
= talloc_reference(state
->recv_iov
, inbuf
);
549 if (tevent_req_nomem(inbuf_ref
, req
)) {
553 /* copy the related buffers */
554 state
->recv_iov
[0] = cur
[0];
555 state
->recv_iov
[1] = cur
[1];
556 state
->recv_iov
[2] = cur
[2];
558 tevent_req_done(req
);
563 num_pending
= talloc_array_length(cli
->conn
.pending
);
564 if (num_pending
== 0) {
565 /* no more pending requests, so we are done for now */
568 req
= cli
->conn
.pending
[0];
569 state
= tevent_req_data(req
, struct smb2cli_req_state
);
572 * add the read_smb request that waits for the
573 * next answer from the server
575 subreq
= read_smb_send(cli
->conn
.pending
, state
->ev
, cli
->fd
);
576 if (subreq
== NULL
) {
577 smb2cli_notify_pending(cli
, NT_STATUS_NO_MEMORY
);
580 tevent_req_set_callback(subreq
, smb2cli_inbuf_received
, cli
);
583 NTSTATUS
smb2cli_req_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
584 struct iovec
**piov
, int body_size
)
586 struct smb2cli_req_state
*state
=
588 struct smb2cli_req_state
);
591 if (tevent_req_is_nterror(req
, &status
)) {
595 status
= NT_STATUS(IVAL(state
->recv_iov
[0].iov_base
, SMB2_HDR_STATUS
));
597 if (body_size
!= 0) {
598 if (body_size
!= SVAL(state
->recv_iov
[1].iov_base
, 0)) {
599 return NT_STATUS_INVALID_NETWORK_RESPONSE
;
603 *piov
= talloc_move(mem_ctx
, &state
->recv_iov
);