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 "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,
184 const uint8_t *fixed,
189 struct tevent_req *req;
190 struct smb2cli_req_state *state;
193 req = tevent_req_create(mem_ctx, &state,
194 struct smb2cli_req_state);
201 state->recv_iov = talloc_zero_array(state, struct iovec, 3);
202 if (state->recv_iov == NULL) {
207 flags |= additional_flags;
208 flags &= ~clear_flags;
210 state->fixed = fixed;
211 state->fixed_len = fixed_len;
213 state->dyn_len = dyn_len;
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);
229 static void smb2cli_writev_done(struct tevent_req *subreq);
231 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
234 struct smb2cli_req_state *state;
235 struct tevent_req *subreq;
237 int i, num_iov, nbt_len;
240 * 1 for the nbt length
241 * per request: HDR, fixed, dyn, padding
242 * -1 because the last one does not need padding
245 iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
247 return NT_STATUS_NO_MEMORY;
253 for (i=0; i<num_reqs; i++) {
258 if (!tevent_req_is_in_progress(reqs[i])) {
259 return NT_STATUS_INTERNAL_ERROR;
262 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
264 if (!cli_state_is_connected(state->cli)) {
265 return NT_STATUS_CONNECTION_DISCONNECTED;
268 if (state->cli->smb2.mid == UINT64_MAX) {
269 return NT_STATUS_CONNECTION_ABORTED;
272 mid = state->cli->smb2.mid;
273 state->cli->smb2.mid += 1;
275 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, mid);
277 iov[num_iov].iov_base = state->hdr;
278 iov[num_iov].iov_len = sizeof(state->hdr);
281 iov[num_iov].iov_base = discard_const(state->fixed);
282 iov[num_iov].iov_len = state->fixed_len;
285 if (state->dyn != NULL) {
286 iov[num_iov].iov_base = discard_const(state->dyn);
287 iov[num_iov].iov_len = state->dyn_len;
291 reqlen = sizeof(state->hdr) + state->fixed_len +
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;
302 SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
306 ret = smb2cli_req_set_pending(reqs[i]);
308 return NT_STATUS_NO_MEMORY;
313 * TODO: Do signing here
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);
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;
326 tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
330 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
331 struct tevent_context *ev,
332 struct cli_state *cli,
334 uint32_t additional_flags,
335 uint32_t clear_flags,
339 const uint8_t *fixed,
344 struct tevent_req *req;
347 req = smb2cli_req_create(mem_ctx, ev, cli, cmd,
348 additional_flags, clear_flags,
350 fixed, fixed_len, dyn, dyn_len);
354 if (!tevent_req_is_in_progress(req)) {
355 return tevent_req_post(req, ev);
357 status = smb2cli_req_compound_submit(&req, 1);
358 if (tevent_req_nterror(req, status)) {
359 return tevent_req_post(req, ev);
364 static void smb2cli_writev_done(struct tevent_req *subreq)
366 struct tevent_req *req =
367 tevent_req_callback_data(subreq,
369 struct smb2cli_req_state *state =
371 struct smb2cli_req_state);
375 nwritten = writev_recv(subreq, &err);
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));
384 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
385 struct iovec **piov, int *pnum_iov)
394 iov = talloc_array(mem_ctx, struct iovec, num_iov);
396 return NT_STATUS_NO_MEMORY;
398 iov[0].iov_base = buf;
401 buflen = smb_len_large(buf) + 4;
404 while (taken < buflen) {
405 size_t len = buflen - taken;
406 uint8_t *hdr = buf + taken;
409 size_t next_command_ofs;
411 struct iovec *iov_tmp;
414 * We need the header plus the body length field
417 if (len < SMB2_HDR_BODY + 2) {
418 DEBUG(10, ("%d bytes left, expected at least %d\n",
419 (int)len, SMB2_HDR_BODY));
422 if (IVAL(hdr, 0) != SMB2_MAGIC) {
423 DEBUG(10, ("Got non-SMB2 PDU: %x\n",
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));
434 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
435 body_size = SVAL(hdr, SMB2_HDR_BODY);
437 if (next_command_ofs != 0) {
438 if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
441 if (next_command_ofs > full_size) {
444 full_size = next_command_ofs;
451 if (body_size > (full_size - SMB2_HDR_BODY)) {
455 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
457 if (iov_tmp == NULL) {
459 return NT_STATUS_NO_MEMORY;
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);
481 return NT_STATUS_INVALID_NETWORK_RESPONSE;
484 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
487 int num_pending = talloc_array_length(cli->conn.pending);
490 for (i=0; i<num_pending; i++) {
491 struct tevent_req *req = cli->conn.pending[i];
492 struct smb2cli_req_state *state =
494 struct smb2cli_req_state);
496 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
503 static void smb2cli_inbuf_received(struct tevent_req *subreq)
505 struct cli_state *cli =
506 tevent_req_callback_data(subreq,
508 TALLOC_CTX *frame = talloc_stackframe();
509 struct tevent_req *req;
510 struct smb2cli_req_state *state;
519 received = read_smb_recv(subreq, frame, &inbuf, &err);
521 if (received == -1) {
523 * We need to close the connection and notify
524 * all pending requests.
526 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
531 status = smb2cli_inbuf_parse_compound(inbuf, frame,
533 if (!NT_STATUS_IS_OK(status)) {
535 * if we cannot parse the incoming pdu,
536 * the connection becomes unusable.
538 * We need to close the connection and notify
539 * all pending requests.
541 smb2cli_notify_pending(cli, status);
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;
551 req = cli_smb2_find_pending(
552 cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
555 * TODO: handle oplock breaks and async responses
559 * We need to close the connection and notify
560 * all pending requests.
562 smb2cli_notify_pending(cli, status);
566 smb2cli_req_unset_pending(req);
567 state = tevent_req_data(req, struct smb2cli_req_state);
570 * There might be more than one response
571 * we need to defer the notifications
573 tevent_req_defer_callback(req, state->ev);
576 * Note: here we use talloc_reference() in a way
577 * that does not expose it to the caller.
579 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
580 if (tevent_req_nomem(inbuf_ref, req)) {
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];
589 tevent_req_done(req);
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 */
602 * If there are no more requests possible,
603 * because we are out of message ids,
604 * we need to disconnect.
606 smb2cli_notify_pending(cli, NT_STATUS_CONNECTION_ABORTED);
609 req = cli->conn.pending[0];
610 state = tevent_req_data(req, struct smb2cli_req_state);
613 * add the read_smb request that waits for the
614 * next answer from the server
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);
621 tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
624 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
625 struct iovec **piov, int body_size)
627 struct smb2cli_req_state *state =
629 struct smb2cli_req_state);
632 if (tevent_req_is_nterror(req, &status)) {
636 status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
638 if (body_size != 0) {
639 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
640 return NT_STATUS_INVALID_NETWORK_RESPONSE;
644 *piov = talloc_move(mem_ctx, &state->recv_iov);