s3:smb2cli_base: add my copyright
[samba.git] / source3 / libsmb / smb2cli_base.c
1 /*
2    Unix SMB/CIFS implementation.
3    smb2 lib
4    Copyright (C) Volker Lendecke 2011
5    Copyright (C) Stefan Metzmacher 2011
6
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.
11
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.
16
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/>.
19 */
20
21 #include "includes.h"
22 #include "client.h"
23 #include "read_smb.h"
24 #include "smb2cli_base.h"
25 #include "lib/async_req/async_sock.h"
26 #include "lib/util/tevent_ntstatus.h"
27
28 struct smb2cli_req_state {
29         struct tevent_context *ev;
30         struct cli_state *cli;
31
32         const uint8_t *fixed;
33         uint16_t fixed_len;
34         const uint8_t *dyn;
35         uint16_t dyn_len;
36
37         uint8_t nbt[4];
38         uint8_t hdr[64];
39         uint8_t pad[7]; /* padding space for compounding */
40
41         /* always an array of 3 talloc elements */
42         struct iovec *recv_iov;
43 };
44
45 static void smb2cli_req_unset_pending(struct tevent_req *req)
46 {
47         struct smb2cli_req_state *state =
48                 tevent_req_data(req,
49                 struct smb2cli_req_state);
50         struct cli_state *cli = state->cli;
51         int num_pending = talloc_array_length(cli->pending);
52         int i;
53
54         talloc_set_destructor(req, NULL);
55
56         if (num_pending == 1) {
57                 /*
58                  * The pending read_smb tevent_req is a child of
59                  * cli->pending. So if nothing is pending anymore, we need to
60                  * delete the socket read fde.
61                  */
62                 TALLOC_FREE(cli->pending);
63                 return;
64         }
65
66         for (i=0; i<num_pending; i++) {
67                 if (req == cli->pending[i]) {
68                         break;
69                 }
70         }
71         if (i == num_pending) {
72                 /*
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->pending.
76                  */
77                 return;
78         }
79
80         /*
81          * Remove ourselves from the cli->pending array
82          */
83         for (; i < (num_pending - 1); i++) {
84                 cli->pending[i] = cli->pending[i+1];
85         }
86
87         /*
88          * No NULL check here, we're shrinking by sizeof(void *), and
89          * talloc_realloc just adjusts the size for this.
90          */
91         cli->pending = talloc_realloc(NULL, cli->pending, struct tevent_req *,
92                                       num_pending - 1);
93         return;
94 }
95
96 static int smb2cli_req_destructor(struct tevent_req *req)
97 {
98         smb2cli_req_unset_pending(req);
99         return 0;
100 }
101
102 static void smb2cli_inbuf_received(struct tevent_req *subreq);
103
104 static bool smb2cli_req_set_pending(struct tevent_req *req)
105 {
106         struct smb2cli_req_state *state =
107                 tevent_req_data(req,
108                 struct smb2cli_req_state);
109         struct cli_state *cli;
110         struct tevent_req **pending;
111         int num_pending;
112         struct tevent_req *subreq;
113
114         cli = state->cli;
115         num_pending = talloc_array_length(cli->pending);
116
117         pending = talloc_realloc(cli, cli->pending, struct tevent_req *,
118                                  num_pending+1);
119         if (pending == NULL) {
120                 return false;
121         }
122         pending[num_pending] = req;
123         cli->pending = pending;
124         talloc_set_destructor(req, smb2cli_req_destructor);
125
126         if (num_pending > 0) {
127                 return true;
128         }
129
130         /*
131          * We're the first ones, add the read_smb request that waits for the
132          * answer from the server
133          */
134         subreq = read_smb_send(cli->pending, state->ev, cli->fd);
135         if (subreq == NULL) {
136                 smb2cli_req_unset_pending(req);
137                 return false;
138         }
139         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
140         return true;
141 }
142
143 static void smb2cli_notify_pending(struct cli_state *cli, NTSTATUS status)
144 {
145         if (cli->fd != -1) {
146                 close(cli->fd);
147                 cli->fd = -1;
148         }
149
150         /*
151          * Cancel all pending requests. We don't do a for-loop walking
152          * cli->pending because that array changes in
153          * cli_smb_req_destructor().
154          */
155         while (talloc_array_length(cli->pending) > 0) {
156                 struct tevent_req *req;
157                 struct smb2cli_req_state *state;
158
159                 req = cli->pending[0];
160                 state = tevent_req_data(req, struct smb2cli_req_state);
161
162                 smb2cli_req_unset_pending(req);
163
164                 /*
165                  * we need to defer the callback, because we may notify more
166                  * then one caller.
167                  */
168                 tevent_req_defer_callback(req, state->ev);
169                 tevent_req_nterror(req, status);
170         }
171 }
172
173 struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx,
174                                       struct tevent_context *ev,
175                                       struct cli_state *cli,
176                                       uint16_t cmd,
177                                       uint32_t flags,
178                                       const uint8_t *fixed,
179                                       uint16_t fixed_len,
180                                       const uint8_t *dyn,
181                                       uint16_t dyn_len)
182 {
183         struct tevent_req *req;
184         struct smb2cli_req_state *state;
185
186         req = tevent_req_create(mem_ctx, &state,
187                                 struct smb2cli_req_state);
188         if (req == NULL) {
189                 return NULL;
190         }
191         state->ev = ev;
192         state->cli = cli;
193
194         state->recv_iov = talloc_zero_array(state, struct iovec, 3);
195         if (state->recv_iov == NULL) {
196                 TALLOC_FREE(req);
197                 return NULL;
198         }
199
200         state->fixed = fixed;
201         state->fixed_len = fixed_len;
202         state->dyn = dyn;
203         state->dyn_len = dyn_len;
204
205         SIVAL(state->hdr, SMB2_HDR_PROTOCOL_ID, SMB2_MAGIC);
206         SSVAL(state->hdr, SMB2_HDR_LENGTH,      SMB2_HDR_BODY);
207         SSVAL(state->hdr, SMB2_HDR_EPOCH,       1);
208         SIVAL(state->hdr, SMB2_HDR_STATUS,      NT_STATUS_V(NT_STATUS_OK));
209         SSVAL(state->hdr, SMB2_HDR_OPCODE,      cmd);
210         SSVAL(state->hdr, SMB2_HDR_CREDIT,      31);
211         SIVAL(state->hdr, SMB2_HDR_FLAGS,       flags);
212         SIVAL(state->hdr, SMB2_HDR_PID,         cli->smb2.pid);
213         SIVAL(state->hdr, SMB2_HDR_TID,         cli->smb2.tid);
214         SBVAL(state->hdr, SMB2_HDR_SESSION_ID,  cli->smb2.uid);
215
216         return req;
217 }
218
219 static void smb2cli_writev_done(struct tevent_req *subreq);
220
221 NTSTATUS smb2cli_req_compound_submit(struct tevent_req **reqs,
222                                      int num_reqs)
223 {
224         struct smb2cli_req_state *state;
225         struct tevent_req *subreq;
226         struct iovec *iov;
227         int i, num_iov, nbt_len;
228
229         /*
230          * 1 for the nbt length
231          * per request: HDR, fixed, dyn, padding
232          * -1 because the last one does not need padding
233          */
234
235         iov = talloc_array(reqs[0], struct iovec, 1 + 4*num_reqs - 1);
236         if (iov == NULL) {
237                 return NT_STATUS_NO_MEMORY;
238         }
239
240         num_iov = 1;
241         nbt_len = 0;
242
243         for (i=0; i<num_reqs; i++) {
244                 size_t reqlen;
245                 bool ret;
246
247                 state = tevent_req_data(reqs[i], struct smb2cli_req_state);
248
249                 SBVAL(state->hdr, SMB2_HDR_MESSAGE_ID, state->cli->smb2.mid++);
250
251                 iov[num_iov].iov_base = state->hdr;
252                 iov[num_iov].iov_len  = sizeof(state->hdr);
253                 num_iov += 1;
254
255                 iov[num_iov].iov_base = discard_const(state->fixed);
256                 iov[num_iov].iov_len  = state->fixed_len;
257                 num_iov += 1;
258
259                 if (state->dyn != NULL) {
260                         iov[num_iov].iov_base = discard_const(state->dyn);
261                         iov[num_iov].iov_len  = state->dyn_len;
262                         num_iov += 1;
263                 }
264
265                 reqlen = sizeof(state->hdr) + state->fixed_len +
266                         state->dyn_len;
267
268                 if (i < num_reqs-1) {
269                         if ((reqlen % 8) > 0) {
270                                 uint8_t pad = 8 - (reqlen % 8);
271                                 iov[num_iov].iov_base = state->pad;
272                                 iov[num_iov].iov_len = pad;
273                                 num_iov += 1;
274                                 reqlen += pad;
275                         }
276                         SIVAL(state->hdr, SMB2_HDR_NEXT_COMMAND, reqlen);
277                 }
278                 nbt_len += reqlen;
279
280                 ret = smb2cli_req_set_pending(reqs[i]);
281                 if (!ret) {
282                         return NT_STATUS_NO_MEMORY;
283                 }
284         }
285
286         /*
287          * TODO: Do signing here
288          */
289
290         state = tevent_req_data(reqs[0], struct smb2cli_req_state);
291         _smb_setlen_large(state->nbt, nbt_len);
292         iov[0].iov_base = state->nbt;
293         iov[0].iov_len  = sizeof(state->nbt);
294
295         subreq = writev_send(state, state->ev, state->cli->outgoing,
296                              state->cli->fd, false, iov, num_iov);
297         if (subreq == NULL) {
298                 return NT_STATUS_NO_MEMORY;
299         }
300         tevent_req_set_callback(subreq, smb2cli_writev_done, reqs[0]);
301         return NT_STATUS_OK;
302 }
303
304 struct tevent_req *smb2cli_req_send(TALLOC_CTX *mem_ctx,
305                                     struct tevent_context *ev,
306                                     struct cli_state *cli,
307                                     uint16_t cmd,
308                                     uint32_t flags,
309                                     const uint8_t *fixed,
310                                     uint16_t fixed_len,
311                                     const uint8_t *dyn,
312                                     uint16_t dyn_len)
313 {
314         struct tevent_req *req;
315         NTSTATUS status;
316
317         req = smb2cli_req_create(mem_ctx, ev, cli, cmd, flags,
318                                  fixed, fixed_len, dyn, dyn_len);
319         if (req == NULL) {
320                 return NULL;
321         }
322         if (!tevent_req_is_in_progress(req)) {
323                 return req;
324         }
325         status = smb2cli_req_compound_submit(&req, 1);
326         if (tevent_req_nterror(req, status)) {
327                 return tevent_req_post(req, ev);
328         }
329         return req;
330 }
331
332 static void smb2cli_writev_done(struct tevent_req *subreq)
333 {
334         struct tevent_req *req =
335                 tevent_req_callback_data(subreq,
336                 struct tevent_req);
337         struct smb2cli_req_state *state =
338                 tevent_req_data(req,
339                 struct smb2cli_req_state);
340         ssize_t nwritten;
341         int err;
342
343         nwritten = writev_recv(subreq, &err);
344         TALLOC_FREE(subreq);
345         if (nwritten == -1) {
346                 /* here, we need to notify all pending requests */
347                 smb2cli_notify_pending(state->cli, map_nt_error_from_unix(err));
348                 return;
349         }
350 }
351
352 static NTSTATUS smb2cli_inbuf_parse_compound(uint8_t *buf, TALLOC_CTX *mem_ctx,
353                                              struct iovec **piov, int *pnum_iov)
354 {
355         struct iovec *iov;
356         int num_iov;
357         size_t buflen;
358         size_t taken;
359
360         num_iov = 1;
361
362         iov = talloc_array(mem_ctx, struct iovec, num_iov);
363         if (iov == NULL) {
364                 return NT_STATUS_NO_MEMORY;
365         }
366         iov[0].iov_base = buf;
367         iov[0].iov_len = 4;
368
369         buflen = smb_len_large(buf) + 4;
370         taken = 4;
371
372         while (taken < buflen) {
373                 size_t len = buflen - taken;
374                 uint8_t *hdr = buf + taken;
375                 struct iovec *cur;
376                 size_t full_size;
377                 size_t next_command_ofs;
378                 uint16_t body_size;
379                 struct iovec *iov_tmp;
380
381                 /*
382                  * We need the header plus the body length field
383                  */
384
385                 if (len < SMB2_HDR_BODY + 2) {
386                         DEBUG(10, ("%d bytes left, expected at least %d\n",
387                                    (int)len, SMB2_HDR_BODY));
388                         goto inval;
389                 }
390                 if (IVAL(hdr, 0) != SMB2_MAGIC) {
391                         DEBUG(10, ("Got non-SMB2 PDU: %x\n",
392                                    IVAL(hdr, 0)));
393                         goto inval;
394                 }
395                 if (SVAL(hdr, 4) != SMB2_HDR_BODY) {
396                         DEBUG(10, ("Got HDR len %d, expected %d\n",
397                                    SVAL(hdr, 4), SMB2_HDR_BODY));
398                         goto inval;
399                 }
400
401                 full_size = len;
402                 next_command_ofs = IVAL(hdr, SMB2_HDR_NEXT_COMMAND);
403                 body_size = SVAL(hdr, SMB2_HDR_BODY);
404
405                 if (next_command_ofs != 0) {
406                         if (next_command_ofs < (SMB2_HDR_BODY + 2)) {
407                                 goto inval;
408                         }
409                         if (next_command_ofs > full_size) {
410                                 goto inval;
411                         }
412                         full_size = next_command_ofs;
413                 }
414                 if (body_size < 2) {
415                         goto inval;
416                 }
417                 body_size &= 0xfffe;
418
419                 if (body_size > (full_size - SMB2_HDR_BODY)) {
420                         goto inval;
421                 }
422
423                 iov_tmp = talloc_realloc(mem_ctx, iov, struct iovec,
424                                          num_iov + 3);
425                 if (iov_tmp == NULL) {
426                         TALLOC_FREE(iov);
427                         return NT_STATUS_NO_MEMORY;
428                 }
429                 iov = iov_tmp;
430                 cur = &iov[num_iov];
431                 num_iov += 3;
432
433                 cur[0].iov_base = hdr;
434                 cur[0].iov_len  = SMB2_HDR_BODY;
435                 cur[1].iov_base = hdr + SMB2_HDR_BODY;
436                 cur[1].iov_len  = body_size;
437                 cur[2].iov_base = hdr + SMB2_HDR_BODY + body_size;
438                 cur[2].iov_len  = full_size - (SMB2_HDR_BODY + body_size);
439
440                 taken += full_size;
441         }
442
443         *piov = iov;
444         *pnum_iov = num_iov;
445         return NT_STATUS_OK;
446
447 inval:
448         TALLOC_FREE(iov);
449         return NT_STATUS_INVALID_NETWORK_RESPONSE;
450 }
451
452 static struct tevent_req *cli_smb2_find_pending(struct cli_state *cli,
453                                                 uint64_t mid)
454 {
455         int num_pending = talloc_array_length(cli->pending);
456         int i;
457
458         for (i=0; i<num_pending; i++) {
459                 struct tevent_req *req = cli->pending[i];
460                 struct smb2cli_req_state *state =
461                         tevent_req_data(req,
462                         struct smb2cli_req_state);
463
464                 if (mid == BVAL(state->hdr, SMB2_HDR_MESSAGE_ID)) {
465                         return req;
466                 }
467         }
468         return NULL;
469 }
470
471 static void smb2cli_inbuf_received(struct tevent_req *subreq)
472 {
473         struct cli_state *cli =
474                 tevent_req_callback_data(subreq,
475                 struct cli_state);
476         TALLOC_CTX *frame = talloc_stackframe();
477         struct tevent_req *req;
478         struct smb2cli_req_state *state;
479         struct iovec *iov;
480         int i, num_iov;
481         NTSTATUS status;
482         uint8_t *inbuf;
483         ssize_t received;
484         int err;
485         size_t num_pending;
486
487         received = read_smb_recv(subreq, frame, &inbuf, &err);
488         TALLOC_FREE(subreq);
489         if (received == -1) {
490                 /*
491                  * We need to close the connection and notify
492                  * all pending requests.
493                  */
494                 smb2cli_notify_pending(cli, map_nt_error_from_unix(err));
495                 TALLOC_FREE(frame);
496                 return;
497         }
498
499         status = smb2cli_inbuf_parse_compound(inbuf, frame,
500                                               &iov, &num_iov);
501         if (!NT_STATUS_IS_OK(status)) {
502                 /*
503                  * if we cannot parse the incoming pdu,
504                  * the connection becomes unusable.
505                  *
506                  * We need to close the connection and notify
507                  * all pending requests.
508                  */
509                 smb2cli_notify_pending(cli, status);
510                 TALLOC_FREE(frame);
511                 return;
512         }
513
514         for (i=1; i<num_iov; i+=3) {
515                 uint8_t *inbuf_ref = NULL;
516                 struct iovec *cur = &iov[i];
517                 uint8_t *inhdr = (uint8_t *)cur[0].iov_base;
518
519                 req = cli_smb2_find_pending(
520                         cli, BVAL(inhdr, SMB2_HDR_MESSAGE_ID));
521                 if (req == NULL) {
522                         /*
523                          * TODO: handle oplock breaks and async responses
524                          */
525
526                         /*
527                          * We need to close the connection and notify
528                          * all pending requests.
529                          */
530                         smb2cli_notify_pending(cli, status);
531                         TALLOC_FREE(frame);
532                         return;
533                 }
534                 smb2cli_req_unset_pending(req);
535                 state = tevent_req_data(req, struct smb2cli_req_state);
536
537                 /*
538                  * There might be more than one response
539                  * we need to defer the notifications
540                  */
541                 tevent_req_defer_callback(req, state->ev);
542
543                 /*
544                  * Note: here we use talloc_reference() in a way
545                  *       that does not expose it to the caller.
546                  */
547                 inbuf_ref = talloc_reference(state->recv_iov, inbuf);
548                 if (tevent_req_nomem(inbuf_ref, req)) {
549                         continue;
550                 }
551
552                 /* copy the related buffers */
553                 state->recv_iov[0] = cur[0];
554                 state->recv_iov[1] = cur[1];
555                 state->recv_iov[2] = cur[2];
556
557                 tevent_req_done(req);
558         }
559
560         TALLOC_FREE(frame);
561
562         num_pending = talloc_array_length(cli->pending);
563         if (num_pending == 0) {
564                 /* no more pending requests, so we are done for now */
565                 return;
566         }
567         req = cli->pending[0];
568         state = tevent_req_data(req, struct smb2cli_req_state);
569
570         /*
571          * add the read_smb request that waits for the
572          * next answer from the server
573          */
574         subreq = read_smb_send(cli->pending, state->ev, cli->fd);
575         if (subreq == NULL) {
576                 smb2cli_notify_pending(cli, NT_STATUS_NO_MEMORY);
577                 return;
578         }
579         tevent_req_set_callback(subreq, smb2cli_inbuf_received, cli);
580 }
581
582 NTSTATUS smb2cli_req_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
583                           struct iovec **piov, int body_size)
584 {
585         struct smb2cli_req_state *state =
586                 tevent_req_data(req,
587                 struct smb2cli_req_state);
588         NTSTATUS status;
589
590         if (tevent_req_is_nterror(req, &status)) {
591                 return status;
592         }
593
594         status = NT_STATUS(IVAL(state->recv_iov[0].iov_base, SMB2_HDR_STATUS));
595
596         if (body_size != 0) {
597                 if (body_size != SVAL(state->recv_iov[1].iov_base, 0)) {
598                         return NT_STATUS_INVALID_NETWORK_RESPONSE;
599                 }
600         }
601         if (piov != NULL) {
602                 *piov = talloc_move(mem_ctx, &state->recv_iov);
603         }
604
605         return status;
606 }