LCOV - code coverage report
Current view: top level - source4/libcli/smb2 - transport.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 228 273 83.5 %
Date: 2024-05-31 13:13:24 Functions: 12 12 100.0 %

          Line data    Source code
       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 "system/network.h"
      24             : #include "libcli/raw/libcliraw.h"
      25             : #include "libcli/raw/raw_proto.h"
      26             : #include "libcli/smb2/smb2.h"
      27             : #include "libcli/smb2/smb2_calls.h"
      28             : #include "lib/socket/socket.h"
      29             : #include "lib/events/events.h"
      30             : #include "../lib/util/dlinklist.h"
      31             : #include "../libcli/smb/smbXcli_base.h"
      32             : #include "librpc/ndr/libndr.h"
      33             : 
      34             : /*
      35             :   destroy a transport
      36             :  */
      37       12709 : static int transport_destructor(struct smb2_transport *transport)
      38             : {
      39       12709 :         smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
      40       12709 :         return 0;
      41             : }
      42             : 
      43             : /*
      44             :   create a transport structure based on an established socket
      45             : */
      46        6185 : struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock,
      47             :                                            TALLOC_CTX *parent_ctx,
      48             :                                            struct smbcli_options *options)
      49             : {
      50         331 :         struct smb2_transport *transport;
      51             : 
      52        6185 :         transport = talloc_zero(parent_ctx, struct smb2_transport);
      53        6185 :         if (!transport) return NULL;
      54             : 
      55        6185 :         transport->ev = sock->event.ctx;
      56        6185 :         transport->options = *options;
      57             : 
      58        6185 :         if (transport->options.max_protocol == PROTOCOL_DEFAULT) {
      59        4145 :                 transport->options.max_protocol = PROTOCOL_LATEST;
      60             :         }
      61             : 
      62        6185 :         if (transport->options.max_protocol < PROTOCOL_SMB2_02) {
      63           0 :                 transport->options.max_protocol = PROTOCOL_LATEST;
      64             :         }
      65             : 
      66        6185 :         TALLOC_FREE(sock->event.fde);
      67        6185 :         TALLOC_FREE(sock->event.te);
      68             : 
      69       12370 :         transport->conn = smbXcli_conn_create(transport,
      70        6185 :                                               sock->sock->fd,
      71             :                                               sock->hostname,
      72             :                                               options->signing,
      73             :                                               0, /* smb1_capabilities */
      74             :                                               &options->client_guid,
      75             :                                               options->smb2_capabilities,
      76        6185 :                                               &options->smb3_capabilities);
      77        6185 :         if (transport->conn == NULL) {
      78           0 :                 talloc_free(transport);
      79           0 :                 return NULL;
      80             :         }
      81        6185 :         sock->sock->fd = -1;
      82        6185 :         TALLOC_FREE(sock);
      83             : 
      84        6185 :         talloc_set_destructor(transport, transport_destructor);
      85             : 
      86        6185 :         return transport;
      87             : }
      88             : 
      89             : /*
      90             :   create a transport structure based on an established socket
      91             : */
      92        6528 : NTSTATUS smb2_transport_raw_init(TALLOC_CTX *mem_ctx,
      93             :                                  struct tevent_context *ev,
      94             :                                  struct smbXcli_conn **_conn,
      95             :                                  const struct smbcli_options *options,
      96             :                                  struct smb2_transport **_transport)
      97             : {
      98        6528 :         struct smb2_transport *transport = NULL;
      99         390 :         enum protocol_types protocol;
     100             : 
     101        6528 :         if (*_conn == NULL) {
     102           0 :                 return NT_STATUS_INVALID_PARAMETER;
     103             :         }
     104             : 
     105        6528 :         protocol = smbXcli_conn_protocol(*_conn);
     106        6528 :         if (protocol < PROTOCOL_SMB2_02) {
     107           0 :                 return NT_STATUS_REVISION_MISMATCH;
     108             :         }
     109             : 
     110        6528 :         transport = talloc_zero(mem_ctx, struct smb2_transport);
     111        6528 :         if (transport == NULL) {
     112           0 :                 return NT_STATUS_NO_MEMORY;
     113             :         }
     114             : 
     115        6528 :         transport->ev = ev;
     116        6528 :         transport->options = *options;
     117        6528 :         transport->conn = talloc_move(transport, _conn);
     118             : 
     119        6528 :         talloc_set_destructor(transport, transport_destructor);
     120        6528 :         *_transport = transport;
     121        6528 :         return NT_STATUS_OK;
     122             : }
     123             : 
     124             : /*
     125             :   mark the transport as dead
     126             : */
     127       13201 : void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
     128             : {
     129       13201 :         if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
     130           0 :                 status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
     131             :         }
     132       13201 :         if (NT_STATUS_IS_OK(status)) {
     133           0 :                 status = NT_STATUS_LOCAL_DISCONNECT;
     134             :         }
     135             : 
     136       13201 :         smbXcli_conn_disconnect(transport->conn, status);
     137       13201 : }
     138             : 
     139             : static void smb2_request_done(struct tevent_req *subreq);
     140             : static void smb2_transport_break_handler(struct tevent_req *subreq);
     141             : 
     142             : /*
     143             :   put a request into the send queue
     144             : */
     145     1153460 : void smb2_transport_send(struct smb2_request *req)
     146             : {
     147         906 :         NTSTATUS status;
     148     1153460 :         struct smb2_transport *transport = req->transport;
     149     1153460 :         struct tevent_req **reqs = transport->compound.reqs;
     150     1153460 :         size_t num_reqs = talloc_array_length(reqs);
     151         906 :         size_t i;
     152     1153460 :         uint16_t cmd = SVAL(req->out.hdr, SMB2_HDR_OPCODE);
     153     1153460 :         uint32_t additional_flags = IVAL(req->out.hdr, SMB2_HDR_FLAGS);
     154     1153460 :         uint32_t clear_flags = 0;
     155     1153460 :         struct smbXcli_tcon *tcon = NULL;
     156     1153460 :         struct smbXcli_session *session = NULL;
     157     1153460 :         bool need_pending_break = false;
     158         906 :         size_t hdr_ofs;
     159         906 :         size_t pdu_len;
     160     1153460 :         DATA_BLOB body = data_blob_null;
     161     1153460 :         DATA_BLOB dyn = data_blob_null;
     162     1153460 :         uint32_t timeout_msec = transport->options.request_timeout * 1000;
     163             : 
     164     1153460 :         if (transport->oplock.handler) {
     165        6930 :                 need_pending_break = true;
     166             :         }
     167             : 
     168     1153460 :         if (transport->lease.handler) {
     169        5004 :                 need_pending_break = true;
     170             :         }
     171             : 
     172     1153460 :         if (transport->break_subreq) {
     173        6936 :                 need_pending_break = false;
     174             :         }
     175             : 
     176     1153460 :         if (need_pending_break) {
     177           0 :                 struct tevent_req *subreq;
     178             : 
     179        1184 :                 subreq = smb2cli_req_create(transport,
     180             :                                             transport->ev,
     181             :                                             transport->conn,
     182             :                                             SMB2_OP_BREAK,
     183             :                                             0, /* additional_flags */
     184             :                                             0, /*clear_flags */
     185             :                                             0, /* timeout_msec */
     186             :                                             NULL, /* tcon */
     187             :                                             NULL, /* session */
     188             :                                             NULL, /* body */
     189             :                                             0, /* body_fixed */
     190             :                                             NULL, /* dyn */
     191             :                                             0, /* dyn_len */
     192             :                                             0); /* max_dyn_len */
     193        1184 :                 if (subreq != NULL) {
     194        1184 :                         smbXcli_req_set_pending(subreq);
     195        1184 :                         tevent_req_set_callback(subreq,
     196             :                                                 smb2_transport_break_handler,
     197             :                                                 transport);
     198        1184 :                         transport->break_subreq = subreq;
     199             :                 }
     200             :         }
     201             : 
     202     1153460 :         if (req->session) {
     203     1152571 :                 session = req->session->smbXcli;
     204             :         }
     205             : 
     206     1153460 :         if (req->tree) {
     207     1152402 :                 tcon = req->tree->smbXcli;
     208             :         }
     209             : 
     210     1153460 :         if (transport->compound.related) {
     211         270 :                 additional_flags |= SMB2_HDR_FLAG_CHAINED;
     212             :         }
     213             : 
     214     1153460 :         hdr_ofs = PTR_DIFF(req->out.hdr, req->out.buffer);
     215     1153460 :         pdu_len = req->out.size - hdr_ofs;
     216     1153460 :         body.data = req->out.body;
     217     1153460 :         body.length = req->out.body_fixed;
     218     1153460 :         dyn.data = req->out.body + req->out.body_fixed;
     219     1153460 :         dyn.length = pdu_len - (SMB2_HDR_BODY + req->out.body_fixed);
     220             : 
     221     2306014 :         req->subreq = smb2cli_req_create(req,
     222             :                                          transport->ev,
     223             :                                          transport->conn,
     224             :                                          cmd,
     225             :                                          additional_flags,
     226             :                                          clear_flags,
     227             :                                          timeout_msec,
     228             :                                          tcon,
     229             :                                          session,
     230     1152554 :                                          body.data, body.length,
     231     1152554 :                                          dyn.data, dyn.length,
     232             :                                          0); /* max_dyn_len */
     233     1153460 :         if (req->subreq == NULL) {
     234           0 :                 req->state = SMB2_REQUEST_ERROR;
     235           0 :                 req->status = NT_STATUS_NO_MEMORY;
     236           0 :                 return;
     237             :         }
     238             : 
     239     1153460 :         if (!tevent_req_is_in_progress(req->subreq)) {
     240           0 :                 req->state = SMB2_REQUEST_ERROR;
     241           0 :                 req->status = NT_STATUS_INTERNAL_ERROR;/* TODO */
     242           0 :                 return;
     243             :         }
     244             : 
     245     1153460 :         tevent_req_set_callback(req->subreq, smb2_request_done, req);
     246             : 
     247     1153460 :         smb2cli_req_set_notify_async(req->subreq);
     248     1153460 :         if (req->credit_charge) {
     249       24050 :                 smb2cli_req_set_credit_charge(req->subreq, req->credit_charge);
     250             :         }
     251             : 
     252     1153460 :         ZERO_STRUCT(req->out);
     253     1153460 :         req->state = SMB2_REQUEST_RECV;
     254             : 
     255     1153460 :         if (num_reqs > 0) {
     256        1086 :                 for (i=0; i < num_reqs; i++) {
     257        1086 :                         if (reqs[i] != NULL) {
     258         602 :                                 continue;
     259             :                         }
     260             : 
     261         484 :                         reqs[i] = req->subreq;
     262         484 :                         i++;
     263         484 :                         break;
     264             :                 }
     265             : 
     266         484 :                 if (i < num_reqs) {
     267         326 :                         return;
     268             :                 }
     269             :         } else {
     270     1152976 :                 reqs = &req->subreq;
     271     1152976 :                 num_reqs = 1;
     272             :         }
     273     1153134 :         status = smb2cli_req_compound_submit(reqs, num_reqs);
     274             : 
     275     1153134 :         TALLOC_FREE(transport->compound.reqs);
     276     1153134 :         transport->compound.related = false;
     277             : 
     278     1153134 :         if (!NT_STATUS_IS_OK(status)) {
     279          35 :                 req->status = status;
     280          35 :                 req->state = SMB2_REQUEST_ERROR;
     281          35 :                 smbXcli_conn_disconnect(transport->conn, status);
     282             :         }
     283             : }
     284             : 
     285     1155024 : static void smb2_request_done(struct tevent_req *subreq)
     286             : {
     287         921 :         struct smb2_request *req =
     288     1155024 :                 tevent_req_callback_data(subreq,
     289             :                 struct smb2_request);
     290         921 :         ssize_t len;
     291         921 :         size_t i;
     292             : 
     293     1155024 :         req->recv_iov = NULL;
     294             : 
     295     1155024 :         req->status = smb2cli_req_recv(req->subreq, req, &req->recv_iov, NULL, 0);
     296     1155024 :         if (NT_STATUS_EQUAL(req->status, NT_STATUS_PENDING)) {
     297        1633 :                 struct timeval endtime = smbXcli_req_endtime(subreq);
     298          16 :                 bool ok;
     299             : 
     300        1633 :                 req->cancel.can_cancel = true;
     301        1633 :                 if (timeval_is_zero(&endtime)) {
     302         829 :                         return;
     303             :                 }
     304             : 
     305         788 :                 ok = tevent_req_set_endtime(
     306         788 :                         subreq, req->transport->ev, endtime);
     307         788 :                 if (!ok) {
     308           0 :                         req->status = NT_STATUS_INTERNAL_ERROR;
     309           0 :                         req->state = SMB2_REQUEST_ERROR;
     310           0 :                         if (req->async.fn) {
     311           0 :                                 req->async.fn(req);
     312             :                         }
     313           0 :                         return;
     314             :                 }
     315         788 :                 return;
     316             :         }
     317     1153391 :         TALLOC_FREE(req->subreq);
     318     1153391 :         if (!NT_STATUS_IS_OK(req->status)) {
     319      180662 :                 if (req->recv_iov == NULL) {
     320          81 :                         req->state = SMB2_REQUEST_ERROR;
     321          81 :                         if (req->async.fn) {
     322           4 :                                 req->async.fn(req);
     323             :                         }
     324          81 :                         return;
     325             :                 }
     326             :         }
     327             : 
     328     1153310 :         len = req->recv_iov[0].iov_len;
     329     3459930 :         for (i=1; i < 3; i++) {
     330     2306620 :                 uint8_t *p = req->recv_iov[i-1].iov_base;
     331     2306620 :                 uint8_t *c1 = req->recv_iov[i].iov_base;
     332     2306620 :                 uint8_t *c2 = p + req->recv_iov[i-1].iov_len;
     333             : 
     334     2306620 :                 len += req->recv_iov[i].iov_len;
     335             : 
     336     2306620 :                 if (req->recv_iov[i].iov_len == 0) {
     337      814527 :                         continue;
     338             :                 }
     339             : 
     340     1492093 :                 if (c1 != c2) {
     341           0 :                         req->status = NT_STATUS_INTERNAL_ERROR;
     342           0 :                         req->state = SMB2_REQUEST_ERROR;
     343           0 :                         if (req->async.fn) {
     344           0 :                                 req->async.fn(req);
     345             :                         }
     346           0 :                         return;
     347             :                 }
     348             :         }
     349             : 
     350     1153310 :         req->in.buffer = req->recv_iov[0].iov_base;
     351     1153310 :         req->in.size = len;
     352     1153310 :         req->in.allocated = req->in.size;
     353             : 
     354     1153310 :         req->in.hdr        =  req->recv_iov[0].iov_base;
     355     1153310 :         req->in.body       =  req->recv_iov[1].iov_base;
     356     1153310 :         req->in.dynamic    =  req->recv_iov[2].iov_base;
     357     1153310 :         req->in.body_fixed =  req->recv_iov[1].iov_len;
     358     1153310 :         req->in.body_size  =  req->in.body_fixed;
     359     1153310 :         req->in.body_size  += req->recv_iov[2].iov_len;
     360             : 
     361     1153310 :         smb2_setup_bufinfo(req);
     362             : 
     363     1153310 :         req->state = SMB2_REQUEST_DONE;
     364     1153310 :         if (req->async.fn) {
     365      618929 :                 req->async.fn(req);
     366             :         }
     367             : }
     368             : 
     369        1164 : static void smb2_transport_break_handler(struct tevent_req *subreq)
     370             : {
     371           0 :         struct smb2_transport *transport =
     372        1164 :                 tevent_req_callback_data(subreq,
     373             :                 struct smb2_transport);
     374           0 :         NTSTATUS status;
     375           0 :         uint8_t *body;
     376        1164 :         uint16_t len = 0;
     377           0 :         bool lease;
     378        1164 :         struct iovec *recv_iov = NULL;
     379             : 
     380        1164 :         transport->break_subreq = NULL;
     381             : 
     382        1164 :         status = smb2cli_req_recv(subreq, transport, &recv_iov, NULL, 0);
     383        1164 :         TALLOC_FREE(subreq);
     384        1164 :         if (!NT_STATUS_IS_OK(status)) {
     385         490 :                 TALLOC_FREE(recv_iov);
     386         490 :                 smb2_transport_dead(transport, status);
     387         490 :                 return;
     388             :         }
     389             : 
     390             :         /*
     391             :          * Setup the subreq to handle the
     392             :          * next incoming SMB2 Break.
     393             :          */
     394         674 :         subreq = smb2cli_req_create(transport,
     395             :                                     transport->ev,
     396             :                                     transport->conn,
     397             :                                     SMB2_OP_BREAK,
     398             :                                     0, /* additional_flags */
     399             :                                     0, /*clear_flags */
     400             :                                     0, /* timeout_msec */
     401             :                                     NULL, /* tcon */
     402             :                                     NULL, /* session */
     403             :                                     NULL, /* body */
     404             :                                     0, /* body_fixed */
     405             :                                     NULL, /* dyn */
     406             :                                     0, /* dyn_len */
     407             :                                     0); /* max_dyn_len */
     408         674 :         if (subreq != NULL) {
     409         674 :                 smbXcli_req_set_pending(subreq);
     410         674 :                 tevent_req_set_callback(subreq,
     411             :                                         smb2_transport_break_handler,
     412             :                                         transport);
     413         674 :                 transport->break_subreq = subreq;
     414             :         }
     415             : 
     416         674 :         body = recv_iov[1].iov_base;
     417             : 
     418         674 :         len = recv_iov[1].iov_len;
     419         674 :         if (recv_iov[1].iov_len >= 2) {
     420         674 :                 len = CVAL(body, 0x00);
     421         674 :                 if (len != recv_iov[1].iov_len) {
     422           0 :                         len = recv_iov[1].iov_len;
     423             :                 }
     424             :         }
     425             : 
     426         674 :         if (len == 24) {
     427         416 :                 lease = false;
     428         258 :         } else if (len == 44) {
     429         258 :                 lease = true;
     430             :         } else {
     431           0 :                 DEBUG(1,("Discarding smb2 oplock reply of invalid size %u\n",
     432             :                         (unsigned)len));
     433           0 :                 TALLOC_FREE(recv_iov);
     434           0 :                 status = NT_STATUS_INVALID_NETWORK_RESPONSE;
     435           0 :                 smb2_transport_dead(transport, status);
     436           0 :                 return;
     437             :         }
     438             : 
     439        1090 :         if (!lease && transport->oplock.handler) {
     440           0 :                 struct smb2_handle h;
     441           0 :                 uint8_t level;
     442             : 
     443         416 :                 level = CVAL(body, 0x02);
     444         416 :                 smb2_pull_handle(body+0x08, &h);
     445             : 
     446         416 :                 TALLOC_FREE(recv_iov);
     447             : 
     448         416 :                 transport->oplock.handler(transport, &h, level,
     449             :                                           transport->oplock.private_data);
     450         516 :         } else if (lease && transport->lease.handler) {
     451           0 :                 struct smb2_lease_break lb;
     452             : 
     453         258 :                 ZERO_STRUCT(lb);
     454         258 :                 lb.new_epoch =                  SVAL(body, 0x2);
     455         258 :                 lb.break_flags =                SVAL(body, 0x4);
     456         258 :                 memcpy(&lb.current_lease.lease_key, body+0x8,
     457             :                     sizeof(struct smb2_lease_key));
     458         258 :                 lb.current_lease.lease_state =  SVAL(body, 0x18);
     459         258 :                 lb.new_lease_state =            SVAL(body, 0x1C);
     460         258 :                 lb.break_reason =               SVAL(body, 0x20);
     461         258 :                 lb.access_mask_hint =           SVAL(body, 0x24);
     462         258 :                 lb.share_mask_hint =            SVAL(body, 0x28);
     463             : 
     464         258 :                 TALLOC_FREE(recv_iov);
     465             : 
     466         258 :                 transport->lease.handler(transport, &lb,
     467             :                     transport->lease.private_data);
     468             :         } else {
     469           0 :                 DEBUG(5,("Got SMB2 %s break with no handler\n",
     470             :                         lease ? "lease" : "oplock"));
     471             :         }
     472         674 :         TALLOC_FREE(recv_iov);
     473             : }
     474             : 
     475         158 : NTSTATUS smb2_transport_compound_start(struct smb2_transport *transport,
     476             :                                        uint32_t num)
     477             : {
     478         158 :         TALLOC_FREE(transport->compound.reqs);
     479         158 :         ZERO_STRUCT(transport->compound);
     480             : 
     481         158 :         transport->compound.reqs = talloc_zero_array(transport,
     482             :                                                      struct tevent_req *,
     483             :                                                      num);
     484         158 :         if (transport->compound.reqs == NULL) {
     485           0 :                 return NT_STATUS_NO_MEMORY;
     486             :         }
     487             : 
     488         158 :         return NT_STATUS_OK;
     489             : }
     490             : 
     491         156 : void smb2_transport_compound_set_related(struct smb2_transport *transport,
     492             :                                          bool related)
     493             : {
     494         156 :         transport->compound.related = related;
     495         156 : }
     496             : 
     497         104 : void smb2_transport_credits_ask_num(struct smb2_transport *transport,
     498             :                                     uint16_t ask_num)
     499             : {
     500         104 :         smb2cli_conn_set_max_credits(transport->conn, ask_num);
     501         104 : }
     502             : 
     503           2 : static void idle_handler(struct tevent_context *ev, 
     504             :                          struct tevent_timer *te, struct timeval t, void *private_data)
     505             : {
     506           2 :         struct smb2_transport *transport = talloc_get_type(private_data,
     507             :                                                            struct smb2_transport);
     508           0 :         struct timeval next;
     509             : 
     510           2 :         transport->idle.func(transport, transport->idle.private_data);
     511             : 
     512           2 :         if (transport->idle.func == NULL) {
     513           2 :                 return;
     514             :         }
     515             : 
     516           2 :         if (!smbXcli_conn_is_connected(transport->conn)) {
     517           2 :                 return;
     518             :         }
     519             : 
     520           0 :         next = timeval_current_ofs_usec(transport->idle.period);
     521           0 :         transport->idle.te = tevent_add_timer(transport->ev,
     522             :                                               transport,
     523             :                                               next,
     524             :                                               idle_handler,
     525             :                                               transport);
     526             : }
     527             : 
     528             : /*
     529             :   setup the idle handler for a transport
     530             :   the period is in microseconds
     531             : */
     532           2 : void smb2_transport_idle_handler(struct smb2_transport *transport, 
     533             :                                  void (*idle_func)(struct smb2_transport *, void *),
     534             :                                  uint64_t period,
     535             :                                  void *private_data)
     536             : {
     537           2 :         TALLOC_FREE(transport->idle.te);
     538           2 :         ZERO_STRUCT(transport->idle);
     539             : 
     540           2 :         if (idle_func == NULL) {
     541           0 :                 return;
     542             :         }
     543             : 
     544           2 :         if (!smbXcli_conn_is_connected(transport->conn)) {
     545           0 :                 return;
     546             :         }
     547             : 
     548           2 :         transport->idle.func = idle_func;
     549           2 :         transport->idle.private_data = private_data;
     550           2 :         transport->idle.period = period;
     551             : 
     552           2 :         transport->idle.te = tevent_add_timer(transport->ev,
     553             :                                               transport,
     554             :                                               timeval_current_ofs_usec(period),
     555             :                                               idle_handler,
     556             :                                               transport);
     557             : }

Generated by: LCOV version 1.14