root/source4/libcli/smb2/transport.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. smb2_transport_event_handler
  2. transport_destructor
  3. smb2_transport_error
  4. smb2_transport_init
  5. smb2_transport_dead
  6. smb2_handle_oplock_break
  7. smb2_transport_finish_recv
  8. smb2_timeout_handler
  9. smb2_request_destructor
  10. smb2_transport_send
  11. idle_handler
  12. smb2_transport_idle_handler

   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, 
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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, 
     /* [<][>][^][v][top][bottom][index][help] */
 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 }

/* [<][>][^][v][top][bottom][index][help] */