~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/libcli/smb2/transport.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
   Unix SMB/CIFS implementation.
 
3
 
 
4
   SMB2 client transport context management functions
 
5
 
 
6
   Copyright (C) Andrew Tridgell 2005
 
7
   
 
8
   This program is free software; you can redistribute it and/or modify
 
9
   it under the terms of the GNU General Public License as published by
 
10
   the Free Software Foundation; either version 3 of the License, or
 
11
   (at your option) any later version.
 
12
   
 
13
   This program is distributed in the hope that it will be useful,
 
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
   GNU General Public License for more details.
 
17
   
 
18
   You should have received a copy of the GNU General Public License
 
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*/
 
21
 
 
22
#include "includes.h"
 
23
#include "libcli/raw/libcliraw.h"
 
24
#include "libcli/raw/raw_proto.h"
 
25
#include "libcli/smb2/smb2.h"
 
26
#include "libcli/smb2/smb2_calls.h"
 
27
#include "lib/socket/socket.h"
 
28
#include "lib/events/events.h"
 
29
#include "lib/stream/packet.h"
 
30
#include "../lib/util/dlinklist.h"
 
31
 
 
32
 
 
33
/*
 
34
  an event has happened on the socket
 
35
*/
 
36
static void smb2_transport_event_handler(struct tevent_context *ev, 
 
37
                                         struct tevent_fd *fde, 
 
38
                                         uint16_t flags, void *private_data)
 
39
{
 
40
        struct smb2_transport *transport = talloc_get_type(private_data,
 
41
                                                           struct smb2_transport);
 
42
        if (flags & EVENT_FD_READ) {
 
43
                packet_recv(transport->packet);
 
44
                return;
 
45
        }
 
46
        if (flags & EVENT_FD_WRITE) {
 
47
                packet_queue_run(transport->packet);
 
48
        }
 
49
}
 
50
 
 
51
/*
 
52
  destroy a transport
 
53
 */
 
54
static int transport_destructor(struct smb2_transport *transport)
 
55
{
 
56
        smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
 
57
        return 0;
 
58
}
 
59
 
 
60
 
 
61
/*
 
62
  handle receive errors
 
63
*/
 
64
static void smb2_transport_error(void *private_data, NTSTATUS status)
 
65
{
 
66
        struct smb2_transport *transport = talloc_get_type(private_data,
 
67
                                                           struct smb2_transport);
 
68
        smb2_transport_dead(transport, status);
 
69
}
 
70
 
 
71
static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob);
 
72
 
 
73
/*
 
74
  create a transport structure based on an established socket
 
75
*/
 
76
struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
 
77
                                           TALLOC_CTX *parent_ctx,
 
78
                                           struct smbcli_options *options)
 
79
{
 
80
        struct smb2_transport *transport;
 
81
 
 
82
        transport = talloc_zero(parent_ctx, struct smb2_transport);
 
83
        if (!transport) return NULL;
 
84
 
 
85
        transport->socket = talloc_steal(transport, sock);
 
86
        transport->options = *options;
 
87
 
 
88
        /* setup the stream -> packet parser */
 
89
        transport->packet = packet_init(transport);
 
90
        if (transport->packet == NULL) {
 
91
                talloc_free(transport);
 
92
                return NULL;
 
93
        }
 
94
        packet_set_private(transport->packet, transport);
 
95
        packet_set_socket(transport->packet, transport->socket->sock);
 
96
        packet_set_callback(transport->packet, smb2_transport_finish_recv);
 
97
        packet_set_full_request(transport->packet, packet_full_request_nbt);
 
98
        packet_set_error_handler(transport->packet, smb2_transport_error);
 
99
        packet_set_event_context(transport->packet, transport->socket->event.ctx);
 
100
        packet_set_nofree(transport->packet);
 
101
 
 
102
        /* take over event handling from the socket layer - it only
 
103
           handles events up until we are connected */
 
104
        talloc_free(transport->socket->event.fde);
 
105
        transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
 
106
                                                    transport->socket,
 
107
                                                    socket_get_fd(transport->socket->sock),
 
108
                                                    EVENT_FD_READ,
 
109
                                                    smb2_transport_event_handler,
 
110
                                                    transport);
 
111
 
 
112
        packet_set_fde(transport->packet, transport->socket->event.fde);
 
113
        packet_set_serialise(transport->packet);
 
114
 
 
115
        talloc_set_destructor(transport, transport_destructor);
 
116
 
 
117
        return transport;
 
118
}
 
119
 
 
120
/*
 
121
  mark the transport as dead
 
122
*/
 
123
void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
 
124
{
 
125
        smbcli_sock_dead(transport->socket);
 
126
 
 
127
        if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
 
128
                status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
 
129
        }
 
130
 
 
131
        /* kill all pending receives */
 
132
        while (transport->pending_recv) {
 
133
                struct smb2_request *req = transport->pending_recv;
 
134
                req->state = SMB2_REQUEST_ERROR;
 
135
                req->status = status;
 
136
                DLIST_REMOVE(transport->pending_recv, req);
 
137
                if (req->async.fn) {
 
138
                        req->async.fn(req);
 
139
                }
 
140
        }
 
141
}
 
142
 
 
143
static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport,
 
144
                                         const DATA_BLOB *blob)
 
145
{
 
146
        uint8_t *hdr;
 
147
        uint16_t opcode;
 
148
 
 
149
        hdr = blob->data+NBT_HDR_SIZE;
 
150
 
 
151
        if (blob->length < (SMB2_MIN_SIZE+0x18)) {
 
152
                DEBUG(1,("Discarding smb2 oplock reply of size %u\n",
 
153
                         (unsigned)blob->length));
 
154
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
 
155
        }
 
156
 
 
157
        opcode  = SVAL(hdr, SMB2_HDR_OPCODE);
 
158
 
 
159
        if (opcode != SMB2_OP_BREAK) {
 
160
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
 
161
        }
 
162
 
 
163
        if (transport->oplock.handler) {
 
164
                uint8_t *body = hdr+SMB2_HDR_BODY;
 
165
                struct smb2_handle h;
 
166
                uint8_t level;
 
167
 
 
168
                level = CVAL(body, 0x02);
 
169
                smb2_pull_handle(body+0x08, &h);
 
170
 
 
171
                transport->oplock.handler(transport, &h, level,
 
172
                                          transport->oplock.private_data);
 
173
        } else {
 
174
                DEBUG(5,("Got SMB2 oplock break with no handler\n"));
 
175
        }
 
176
 
 
177
        return NT_STATUS_OK;
 
178
}
 
179
 
 
180
/*
 
181
  we have a full request in our receive buffer - match it to a pending request
 
182
  and process
 
183
 */
 
184
static NTSTATUS smb2_transport_finish_recv(void *private_data, DATA_BLOB blob)
 
185
{
 
186
        struct smb2_transport *transport = talloc_get_type(private_data,
 
187
                                                             struct smb2_transport);
 
188
        uint8_t *buffer, *hdr;
 
189
        int len;
 
190
        struct smb2_request *req = NULL;
 
191
        uint64_t seqnum;
 
192
        uint32_t flags;
 
193
        uint16_t buffer_code;
 
194
        uint32_t dynamic_size;
 
195
        uint32_t i;
 
196
        NTSTATUS status;
 
197
 
 
198
        buffer = blob.data;
 
199
        len = blob.length;
 
200
 
 
201
        hdr = buffer+NBT_HDR_SIZE;
 
202
 
 
203
        if (len < SMB2_MIN_SIZE) {
 
204
                DEBUG(1,("Discarding smb2 reply of size %d\n", len));
 
205
                goto error;
 
206
        }
 
207
 
 
208
        flags   = IVAL(hdr, SMB2_HDR_FLAGS);
 
209
        seqnum  = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
 
210
 
 
211
        /* see MS-SMB2 3.2.5.19 */
 
212
        if (seqnum == UINT64_MAX) {
 
213
                return smb2_handle_oplock_break(transport, &blob);
 
214
        }
 
215
 
 
216
        /* match the incoming request against the list of pending requests */
 
217
        for (req=transport->pending_recv; req; req=req->next) {
 
218
                if (req->seqnum == seqnum) break;
 
219
        }
 
220
 
 
221
        if (!req) {
 
222
                DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n", 
 
223
                         (long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
 
224
                goto error;
 
225
        }
 
226
 
 
227
        /* fill in the 'in' portion of the matching request */
 
228
        req->in.buffer = buffer;
 
229
        talloc_steal(req, buffer);
 
230
        req->in.size = len;
 
231
        req->in.allocated = req->in.size;
 
232
 
 
233
        req->in.hdr       = hdr;
 
234
        req->in.body      = hdr+SMB2_HDR_BODY;
 
235
        req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
 
236
        req->status       = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
 
237
 
 
238
        if ((flags & SMB2_HDR_FLAG_ASYNC) &&
 
239
            NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
 
240
                req->cancel.can_cancel = true;
 
241
                req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID);
 
242
                for (i=0; i< req->cancel.do_cancel; i++) {
 
243
                        smb2_cancel(req);
 
244
                }
 
245
                talloc_free(buffer);
 
246
                return NT_STATUS_OK;
 
247
        }
 
248
 
 
249
        if (req->session && req->session->signing_active) {
 
250
                status = smb2_check_signature(&req->in, 
 
251
                                              req->session->session_key);
 
252
                if (!NT_STATUS_IS_OK(status)) {
 
253
                        /* the spec says to ignore packets with a bad signature */
 
254
                        talloc_free(buffer);
 
255
                        return status;
 
256
                }
 
257
        }
 
258
 
 
259
        buffer_code = SVAL(req->in.body, 0);
 
260
        req->in.body_fixed = (buffer_code & ~1);
 
261
        req->in.dynamic = NULL;
 
262
        dynamic_size = req->in.body_size - req->in.body_fixed;
 
263
        if (dynamic_size != 0 && (buffer_code & 1)) {
 
264
                req->in.dynamic = req->in.body + req->in.body_fixed;
 
265
                if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
 
266
                        DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n", 
 
267
                                 dynamic_size));
 
268
                        goto error;
 
269
                }
 
270
        }
 
271
 
 
272
        smb2_setup_bufinfo(req);
 
273
 
 
274
        DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
 
275
        dump_data(5, req->in.body, req->in.body_size);
 
276
 
 
277
        /* if this request has an async handler then call that to
 
278
           notify that the reply has been received. This might destroy
 
279
           the request so it must happen last */
 
280
        DLIST_REMOVE(transport->pending_recv, req);
 
281
        req->state = SMB2_REQUEST_DONE;
 
282
        if (req->async.fn) {
 
283
                req->async.fn(req);
 
284
        }
 
285
        return NT_STATUS_OK;
 
286
 
 
287
error:
 
288
        dump_data(5, buffer, len);
 
289
        if (req) {
 
290
                DLIST_REMOVE(transport->pending_recv, req);
 
291
                req->state = SMB2_REQUEST_ERROR;
 
292
                if (req->async.fn) {
 
293
                        req->async.fn(req);
 
294
                }
 
295
        } else {
 
296
                talloc_free(buffer);
 
297
        }
 
298
        return NT_STATUS_UNSUCCESSFUL;
 
299
}
 
300
 
 
301
/*
 
302
  handle timeouts of individual smb requests
 
303
*/
 
304
static void smb2_timeout_handler(struct tevent_context *ev, struct tevent_timer *te, 
 
305
                                 struct timeval t, void *private_data)
 
306
{
 
307
        struct smb2_request *req = talloc_get_type(private_data, struct smb2_request);
 
308
 
 
309
        if (req->state == SMB2_REQUEST_RECV) {
 
310
                DLIST_REMOVE(req->transport->pending_recv, req);
 
311
        }
 
312
        req->status = NT_STATUS_IO_TIMEOUT;
 
313
        req->state = SMB2_REQUEST_ERROR;
 
314
        if (req->async.fn) {
 
315
                req->async.fn(req);
 
316
        }
 
317
}
 
318
 
 
319
 
 
320
/*
 
321
  destroy a request
 
322
*/
 
323
static int smb2_request_destructor(struct smb2_request *req)
 
324
{
 
325
        if (req->state == SMB2_REQUEST_RECV) {
 
326
                DLIST_REMOVE(req->transport->pending_recv, req);
 
327
        }
 
328
        return 0;
 
329
}
 
330
 
 
331
 
 
332
/*
 
333
  put a request into the send queue
 
334
*/
 
335
void smb2_transport_send(struct smb2_request *req)
 
336
{
 
337
        DATA_BLOB blob;
 
338
        NTSTATUS status;
 
339
 
 
340
        _smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
 
341
 
 
342
        DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
 
343
        dump_data(5, req->out.body, req->out.body_size);
 
344
 
 
345
        /* check if the transport is dead */
 
346
        if (req->transport->socket->sock == NULL) {
 
347
                req->state = SMB2_REQUEST_ERROR;
 
348
                req->status = NT_STATUS_NET_WRITE_FAULT;
 
349
                return;
 
350
        }
 
351
 
 
352
        /* possibly sign the message */
 
353
        if (req->session && req->session->signing_active) {
 
354
                status = smb2_sign_message(&req->out, req->session->session_key);
 
355
                if (!NT_STATUS_IS_OK(status)) {
 
356
                        req->state = SMB2_REQUEST_ERROR;
 
357
                        req->status = status;
 
358
                        return;
 
359
                }
 
360
        }
 
361
        
 
362
        blob = data_blob_const(req->out.buffer, req->out.size);
 
363
        status = packet_send(req->transport->packet, blob);
 
364
        if (!NT_STATUS_IS_OK(status)) {
 
365
                req->state = SMB2_REQUEST_ERROR;
 
366
                req->status = status;
 
367
                return;
 
368
        }
 
369
 
 
370
        req->state = SMB2_REQUEST_RECV;
 
371
        DLIST_ADD(req->transport->pending_recv, req);
 
372
 
 
373
        /* add a timeout */
 
374
        if (req->transport->options.request_timeout) {
 
375
                event_add_timed(req->transport->socket->event.ctx, req, 
 
376
                                timeval_current_ofs(req->transport->options.request_timeout, 0), 
 
377
                                smb2_timeout_handler, req);
 
378
        }
 
379
 
 
380
        talloc_set_destructor(req, smb2_request_destructor);
 
381
}
 
382
 
 
383
static void idle_handler(struct tevent_context *ev, 
 
384
                         struct tevent_timer *te, struct timeval t, void *private_data)
 
385
{
 
386
        struct smb2_transport *transport = talloc_get_type(private_data,
 
387
                                                           struct smb2_transport);
 
388
        struct timeval next = timeval_add(&t, 0, transport->idle.period);
 
389
        transport->socket->event.te = event_add_timed(transport->socket->event.ctx, 
 
390
                                                      transport,
 
391
                                                      next,
 
392
                                                      idle_handler, transport);
 
393
        transport->idle.func(transport, transport->idle.private_data);
 
394
}
 
395
 
 
396
/*
 
397
  setup the idle handler for a transport
 
398
  the period is in microseconds
 
399
*/
 
400
void smb2_transport_idle_handler(struct smb2_transport *transport, 
 
401
                                 void (*idle_func)(struct smb2_transport *, void *),
 
402
                                 uint64_t period,
 
403
                                 void *private_data)
 
404
{
 
405
        transport->idle.func = idle_func;
 
406
        transport->idle.private_data = private_data;
 
407
        transport->idle.period = period;
 
408
 
 
409
        if (transport->socket->event.te != NULL) {
 
410
                talloc_free(transport->socket->event.te);
 
411
        }
 
412
 
 
413
        transport->socket->event.te = event_add_timed(transport->socket->event.ctx, 
 
414
                                                      transport,
 
415
                                                      timeval_current_ofs(0, period),
 
416
                                                      idle_handler, transport);
 
417
}