LCOV - code coverage report
Current view: top level - source3/smbd - smb2_break.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 151 227 66.5 %
Date: 2024-02-28 12:06:22 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Core SMB2 server
       4             : 
       5             :    Copyright (C) Stefan Metzmacher 2009
       6             :    Copyright (C) Jeremy Allison 2010
       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 "smbd/smbd.h"
      24             : #include "smbd/globals.h"
      25             : #include "../libcli/smb/smb_common.h"
      26             : #include "../lib/util/tevent_ntstatus.h"
      27             : #include "locking/leases_db.h"
      28             : 
      29             : #undef DBGC_CLASS
      30             : #define DBGC_CLASS DBGC_SMB2
      31             : 
      32             : static NTSTATUS smbd_smb2_request_process_lease_break(
      33             :         struct smbd_smb2_request *req);
      34             : 
      35             : static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
      36             :                                                       struct tevent_context *ev,
      37             :                                                       struct smbd_smb2_request *smb2req,
      38             :                                                       struct files_struct *in_fsp,
      39             :                                                       uint8_t in_oplock_level);
      40             : static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
      41             :                                             uint8_t *out_oplock_level);
      42             : 
      43             : static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq);
      44         274 : NTSTATUS smbd_smb2_request_process_break(struct smbd_smb2_request *req)
      45             : {
      46           0 :         NTSTATUS status;
      47           0 :         const uint8_t *inbody;
      48           0 :         uint8_t in_oplock_level;
      49           0 :         uint64_t in_file_id_persistent;
      50           0 :         uint64_t in_file_id_volatile;
      51           0 :         struct files_struct *in_fsp;
      52           0 :         struct tevent_req *subreq;
      53             : 
      54         274 :         status = smbd_smb2_request_verify_sizes(req, 0x18);
      55         274 :         if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) {
      56             :                 /*
      57             :                  * Retry as a lease break
      58             :                  */
      59         142 :                 return smbd_smb2_request_process_lease_break(req);
      60             :         }
      61         132 :         if (!NT_STATUS_IS_OK(status)) {
      62           0 :                 return smbd_smb2_request_error(req, status);
      63             :         }
      64         132 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      65             : 
      66         132 :         in_oplock_level         = CVAL(inbody, 0x02);
      67             : 
      68             :         /* 0x03 1 bytes reserved */
      69             :         /* 0x04 4 bytes reserved */
      70         132 :         in_file_id_persistent           = BVAL(inbody, 0x08);
      71         132 :         in_file_id_volatile             = BVAL(inbody, 0x10);
      72             : 
      73         132 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
      74         132 :         if (in_fsp == NULL) {
      75           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
      76             :         }
      77             : 
      78             :         /* Are we awaiting a break message ? */
      79         132 :         if (in_fsp->oplock_timeout == NULL) {
      80           6 :                 return smbd_smb2_request_error(
      81             :                         req, NT_STATUS_INVALID_OPLOCK_PROTOCOL);
      82             :         }
      83             : 
      84         126 :         if (in_oplock_level != SMB2_OPLOCK_LEVEL_NONE &&
      85             :             in_oplock_level != SMB2_OPLOCK_LEVEL_II) {
      86           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      87             :         }
      88             : 
      89         126 :         subreq = smbd_smb2_oplock_break_send(req, req->sconn->ev_ctx,
      90             :                                              req, in_fsp, in_oplock_level);
      91         126 :         if (subreq == NULL) {
      92           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
      93             :         }
      94         126 :         tevent_req_set_callback(subreq, smbd_smb2_request_oplock_break_done, req);
      95             : 
      96         126 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
      97             : }
      98             : 
      99         126 : static void smbd_smb2_request_oplock_break_done(struct tevent_req *subreq)
     100             : {
     101         126 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     102             :                                         struct smbd_smb2_request);
     103           0 :         const uint8_t *inbody;
     104           0 :         uint64_t in_file_id_persistent;
     105           0 :         uint64_t in_file_id_volatile;
     106         126 :         uint8_t out_oplock_level = 0;
     107           0 :         DATA_BLOB outbody;
     108           0 :         NTSTATUS status;
     109           0 :         NTSTATUS error; /* transport error */
     110             : 
     111         126 :         status = smbd_smb2_oplock_break_recv(subreq, &out_oplock_level);
     112         126 :         TALLOC_FREE(subreq);
     113         126 :         if (!NT_STATUS_IS_OK(status)) {
     114           0 :                 error = smbd_smb2_request_error(req, status);
     115           0 :                 if (!NT_STATUS_IS_OK(error)) {
     116           0 :                         smbd_server_connection_terminate(req->xconn,
     117             :                                                          nt_errstr(error));
     118           0 :                         return;
     119             :                 }
     120           0 :                 return;
     121             :         }
     122             : 
     123         126 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
     124             : 
     125         126 :         in_file_id_persistent   = BVAL(inbody, 0x08);
     126         126 :         in_file_id_volatile     = BVAL(inbody, 0x10);
     127             : 
     128         126 :         outbody = smbd_smb2_generate_outbody(req, 0x18);
     129         126 :         if (outbody.data == NULL) {
     130           0 :                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     131           0 :                 if (!NT_STATUS_IS_OK(error)) {
     132           0 :                         smbd_server_connection_terminate(req->xconn,
     133             :                                                          nt_errstr(error));
     134           0 :                         return;
     135             :                 }
     136           0 :                 return;
     137             :         }
     138             : 
     139         126 :         SSVAL(outbody.data, 0x00, 0x18);        /* struct size */
     140         126 :         SCVAL(outbody.data, 0x02,
     141             :               out_oplock_level);                /* SMB2 oplock level */
     142         126 :         SCVAL(outbody.data, 0x03, 0);           /* reserved */
     143         126 :         SIVAL(outbody.data, 0x04, 0);           /* reserved */
     144         126 :         SBVAL(outbody.data, 0x08,
     145             :               in_file_id_persistent);           /* file id (persistent) */
     146         126 :         SBVAL(outbody.data, 0x10,
     147             :               in_file_id_volatile);             /* file id (volatile) */
     148             : 
     149         126 :         error = smbd_smb2_request_done(req, outbody, NULL);
     150         126 :         if (!NT_STATUS_IS_OK(error)) {
     151           0 :                 smbd_server_connection_terminate(req->xconn,
     152             :                                                  nt_errstr(error));
     153           0 :                 return;
     154             :         }
     155             : }
     156             : 
     157             : struct smbd_smb2_oplock_break_state {
     158             :         struct smbd_smb2_request *smb2req;
     159             :         uint8_t out_oplock_level; /* SMB2 oplock level. */
     160             : };
     161             : 
     162         126 : static struct tevent_req *smbd_smb2_oplock_break_send(TALLOC_CTX *mem_ctx,
     163             :                                                       struct tevent_context *ev,
     164             :                                                       struct smbd_smb2_request *smb2req,
     165             :                                                       struct files_struct *fsp,
     166             :                                                       uint8_t in_oplock_level)
     167             : {
     168           0 :         struct tevent_req *req;
     169           0 :         struct smbd_smb2_oplock_break_state *state;
     170           0 :         struct smb_request *smbreq;
     171         126 :         int oplocklevel = map_smb2_oplock_levels_to_samba(in_oplock_level);
     172         126 :         bool break_to_none = (oplocklevel == NO_OPLOCK);
     173           0 :         bool result;
     174             : 
     175         126 :         req = tevent_req_create(mem_ctx, &state,
     176             :                                 struct smbd_smb2_oplock_break_state);
     177         126 :         if (req == NULL) {
     178           0 :                 return NULL;
     179             :         }
     180         126 :         state->smb2req = smb2req;
     181         126 :         state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
     182             : 
     183         126 :         DEBUG(10,("smbd_smb2_oplock_break_send: %s - %s, "
     184             :                   "samba level %d\n",
     185             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp),
     186             :                   oplocklevel));
     187             : 
     188         126 :         smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
     189         126 :         if (tevent_req_nomem(smbreq, req)) {
     190           0 :                 return tevent_req_post(req, ev);
     191             :         }
     192             : 
     193         126 :         DEBUG(5,("smbd_smb2_oplock_break_send: got SMB2 oplock break (%u) from client "
     194             :                 "for file %s, %s\n",
     195             :                 (unsigned int)in_oplock_level,
     196             :                 fsp_str_dbg(fsp),
     197             :                 fsp_fnum_dbg(fsp)));
     198             : 
     199         126 :         if ((fsp->sent_oplock_break == BREAK_TO_NONE_SENT) ||
     200             :                         (break_to_none)) {
     201          18 :                 result = remove_oplock(fsp);
     202          18 :                 state->out_oplock_level = SMB2_OPLOCK_LEVEL_NONE;
     203             :         } else {
     204         108 :                 result = downgrade_oplock(fsp);
     205         108 :                 state->out_oplock_level = SMB2_OPLOCK_LEVEL_II;
     206             :         }
     207             : 
     208         126 :         if (!result) {
     209           0 :                 DEBUG(0, ("smbd_smb2_oplock_break_send: error in removing "
     210             :                         "oplock on file %s\n", fsp_str_dbg(fsp)));
     211             :                 /* Hmmm. Is this panic justified? */
     212           0 :                 smb_panic("internal tdb error");
     213             :         }
     214             : 
     215         126 :         tevent_req_done(req);
     216         126 :         return tevent_req_post(req, ev);
     217             : }
     218             : 
     219         126 : static NTSTATUS smbd_smb2_oplock_break_recv(struct tevent_req *req,
     220             :                                             uint8_t *out_oplock_level)
     221             : {
     222           0 :         NTSTATUS status;
     223           0 :         struct smbd_smb2_oplock_break_state *state =
     224         126 :                 tevent_req_data(req,
     225             :                 struct smbd_smb2_oplock_break_state);
     226             : 
     227         126 :         if (tevent_req_is_nterror(req, &status)) {
     228           0 :                 tevent_req_received(req);
     229           0 :                 return status;
     230             :         }
     231             : 
     232         126 :         *out_oplock_level = state->out_oplock_level;
     233             : 
     234         126 :         tevent_req_received(req);
     235         126 :         return NT_STATUS_OK;
     236             : }
     237             : 
     238             : static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq);
     239             : 
     240             : static struct tevent_req *smbd_smb2_lease_break_send(
     241             :         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     242             :         struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
     243             :         uint32_t in_lease_state);
     244             : static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
     245             :                                            uint32_t *out_lease_state);
     246             : 
     247             : 
     248         142 : static NTSTATUS smbd_smb2_request_process_lease_break(
     249             :         struct smbd_smb2_request *req)
     250             : {
     251           0 :         NTSTATUS status;
     252           0 :         const uint8_t *inbody;
     253           0 :         struct smb2_lease_key in_lease_key;
     254           0 :         uint32_t in_lease_state;
     255           0 :         struct tevent_req *subreq;
     256             : 
     257         142 :         status = smbd_smb2_request_verify_sizes(req, 0x24);
     258         142 :         if (!NT_STATUS_IS_OK(status)) {
     259           0 :                 return smbd_smb2_request_error(req, status);
     260             :         }
     261             : 
     262         142 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
     263             : 
     264         142 :         in_lease_key.data[0] = BVAL(inbody, 8);
     265         142 :         in_lease_key.data[1] = BVAL(inbody, 16);
     266         142 :         in_lease_state = IVAL(inbody, 24);
     267             : 
     268         142 :         subreq = smbd_smb2_lease_break_send(req, req->sconn->ev_ctx, req,
     269             :                                             in_lease_key, in_lease_state);
     270         142 :         if (subreq == NULL) {
     271           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     272             :         }
     273         142 :         tevent_req_set_callback(subreq, smbd_smb2_request_lease_break_done, req);
     274             : 
     275         142 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     276             : }
     277             : 
     278         142 : static void smbd_smb2_request_lease_break_done(struct tevent_req *subreq)
     279             : {
     280         142 :         struct smbd_smb2_request *req = tevent_req_callback_data(
     281             :                 subreq, struct smbd_smb2_request);
     282           0 :         const uint8_t *inbody;
     283           0 :         struct smb2_lease_key in_lease_key;
     284         142 :         uint32_t out_lease_state = 0;
     285           0 :         DATA_BLOB outbody;
     286           0 :         NTSTATUS status;
     287           0 :         NTSTATUS error; /* transport error */
     288             : 
     289         142 :         status = smbd_smb2_lease_break_recv(subreq, &out_lease_state);
     290         142 :         TALLOC_FREE(subreq);
     291         142 :         if (!NT_STATUS_IS_OK(status)) {
     292          22 :                 error = smbd_smb2_request_error(req, status);
     293          22 :                 if (!NT_STATUS_IS_OK(error)) {
     294           0 :                         smbd_server_connection_terminate(req->xconn,
     295             :                                                          nt_errstr(error));
     296          22 :                         return;
     297             :                 }
     298          22 :                 return;
     299             :         }
     300             : 
     301         120 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
     302             : 
     303         120 :         in_lease_key.data[0] = BVAL(inbody, 8);
     304         120 :         in_lease_key.data[1] = BVAL(inbody, 16);
     305             : 
     306         120 :         outbody = smbd_smb2_generate_outbody(req, 0x24);
     307         120 :         if (outbody.data == NULL) {
     308           0 :                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     309           0 :                 if (!NT_STATUS_IS_OK(error)) {
     310           0 :                         smbd_server_connection_terminate(req->xconn,
     311             :                                                          nt_errstr(error));
     312           0 :                         return;
     313             :                 }
     314           0 :                 return;
     315             :         }
     316             : 
     317         120 :         SSVAL(outbody.data, 0x00, 0x24);        /* struct size */
     318         120 :         SSVAL(outbody.data, 0x02, 0);           /* reserved */
     319         120 :         SIVAL(outbody.data, 0x04, 0);           /* flags, must be 0 */
     320         120 :         SBVAL(outbody.data, 0x08, in_lease_key.data[0]);
     321         120 :         SBVAL(outbody.data, 0x10, in_lease_key.data[1]);
     322         120 :         SIVAL(outbody.data, 0x18, out_lease_state);
     323         120 :         SBVAL(outbody.data, 0x1c, 0);           /* leaseduration, must be 0 */
     324             : 
     325         120 :         error = smbd_smb2_request_done(req, outbody, NULL);
     326         120 :         if (!NT_STATUS_IS_OK(error)) {
     327           0 :                 smbd_server_connection_terminate(req->xconn,
     328             :                                                  nt_errstr(error));
     329           0 :                 return;
     330             :         }
     331             : }
     332             : 
     333             : struct smbd_smb2_lease_break_state {
     334             :         uint32_t lease_state;
     335             : };
     336             : 
     337             : struct lease_lookup_state {
     338             :         TALLOC_CTX *mem_ctx;
     339             :         /* Return parameters. */
     340             :         uint32_t num_file_ids;
     341             :         struct file_id *ids;
     342             :         NTSTATUS status;
     343             : };
     344             : 
     345         140 : static void lease_parser(
     346             :         uint32_t num_files,
     347             :         const struct leases_db_file *files,
     348             :         void *private_data)
     349             : {
     350         140 :         struct lease_lookup_state *lls =
     351             :                 (struct lease_lookup_state *)private_data;
     352             : 
     353         140 :         lls->status = NT_STATUS_OK;
     354         140 :         lls->num_file_ids = num_files;
     355         140 :         lls->status = leases_db_copy_file_ids(lls->mem_ctx,
     356             :                                 num_files,
     357             :                                 files,
     358             :                                 &lls->ids);
     359         140 : }
     360             : 
     361         142 : static struct tevent_req *smbd_smb2_lease_break_send(
     362             :         TALLOC_CTX *mem_ctx, struct tevent_context *ev,
     363             :         struct smbd_smb2_request *smb2_req, struct smb2_lease_key in_lease_key,
     364             :         uint32_t in_lease_state)
     365             : {
     366           0 :         struct tevent_req *req;
     367           0 :         struct smbd_smb2_lease_break_state *state;
     368         142 :         struct lease_lookup_state lls = {.mem_ctx = mem_ctx};
     369           0 :         NTSTATUS status;
     370             : 
     371         142 :         req = tevent_req_create(mem_ctx, &state,
     372             :                                 struct smbd_smb2_lease_break_state);
     373         142 :         if (req == NULL) {
     374           0 :                 return NULL;
     375             :         }
     376         142 :         state->lease_state = in_lease_state;
     377             : 
     378             :         /* Find any file ids with this lease key. */
     379         142 :         status = leases_db_parse(&smb2_req->xconn->smb2.client.guid,
     380             :                                  &in_lease_key,
     381             :                                  lease_parser,
     382             :                                  &lls);
     383             : 
     384         142 :         if (!NT_STATUS_IS_OK(status)) {
     385           2 :                 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
     386           2 :                         status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
     387           2 :                         DEBUG(10, ("No record for lease key found\n"));
     388             :                 }
     389           2 :                 tevent_req_nterror(req, status);
     390           2 :                 return tevent_req_post(req, ev);
     391             :         }
     392             : 
     393         140 :         if (tevent_req_nterror(req, lls.status)) {
     394           0 :                 return tevent_req_post(req, ev);
     395             :         }
     396             : 
     397         140 :         if (lls.num_file_ids == 0) {
     398           0 :                 tevent_req_nterror(req, NT_STATUS_OBJECT_NAME_NOT_FOUND);
     399           0 :                 return tevent_req_post(req, ev);
     400             :         }
     401             : 
     402         140 :         status = downgrade_lease(smb2_req->xconn->client,
     403             :                                 lls.num_file_ids,
     404         140 :                                 lls.ids,
     405             :                                 &in_lease_key,
     406             :                                 in_lease_state);
     407             : 
     408         140 :         if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_BREAK_IN_PROGRESS)) {
     409           8 :                 tevent_req_done(req);
     410           8 :                 return tevent_req_post(req, ev);
     411             :         }
     412         132 :         if (tevent_req_nterror(req, status)) {
     413          20 :                 DEBUG(10, ("downgrade_lease returned %s\n",
     414             :                            nt_errstr(status)));
     415          20 :                 return tevent_req_post(req, ev);
     416             :         }
     417             : 
     418         112 :         tevent_req_done(req);
     419         112 :         return tevent_req_post(req, ev);
     420             : }
     421             : 
     422         142 : static NTSTATUS smbd_smb2_lease_break_recv(struct tevent_req *req,
     423             :                                            uint32_t *out_lease_state)
     424             : {
     425         142 :         struct smbd_smb2_lease_break_state *state = tevent_req_data(
     426             :                 req, struct smbd_smb2_lease_break_state);
     427           0 :         NTSTATUS status;
     428             : 
     429         142 :         if (tevent_req_is_nterror(req, &status)) {
     430          22 :                 return status;
     431             :         }
     432         120 :         *out_lease_state = state->lease_state;
     433         120 :         return NT_STATUS_OK;
     434             : }
     435             : 
     436             : /*********************************************************
     437             :  Create and send an asynchronous
     438             :  SMB2 OPLOCK_BREAK_NOTIFICATION.
     439             : *********************************************************/
     440             : 
     441         426 : void send_break_message_smb2(files_struct *fsp,
     442             :                              uint32_t break_from,
     443             :                              uint32_t break_to)
     444             : {
     445         426 :         struct smbXsrv_client *client =
     446         426 :                 fsp->conn->sconn->client;
     447           0 :         NTSTATUS status;
     448             : 
     449         426 :         if (!NT_STATUS_IS_OK(fsp->op->status)) {
     450           0 :                 DBG_DEBUG("skip oplock break for file %s, %s, "
     451             :                           "smb2 level %u fsp status=%s\n",
     452             :                           fsp_str_dbg(fsp),
     453             :                           fsp_fnum_dbg(fsp),
     454             :                           (unsigned int)break_to,
     455             :                           nt_errstr(fsp->op->status));
     456           0 :                 return;
     457             :         }
     458             : 
     459         426 :         DBG_DEBUG("sending oplock break "
     460             :                   "for file %s, %s, smb2 level %u\n",
     461             :                   fsp_str_dbg(fsp),
     462             :                   fsp_fnum_dbg(fsp),
     463             :                   (unsigned int)break_to);
     464             : 
     465         426 :         if (fsp->oplock_type == LEASE_OPLOCK) {
     466         184 :                 uint32_t break_flags = 0;
     467           0 :                 uint16_t new_epoch;
     468             : 
     469         184 :                 if (fsp->lease->lease.lease_state != SMB2_LEASE_NONE) {
     470         162 :                         break_flags = SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED;
     471             :                 }
     472             : 
     473         184 :                 if (fsp->lease->lease.lease_version > 1) {
     474          70 :                         new_epoch = fsp->lease->lease.lease_epoch;
     475             :                 } else {
     476         114 :                         new_epoch = 0;
     477             :                 }
     478             : 
     479         184 :                 status = smbd_smb2_send_lease_break(client, new_epoch, break_flags,
     480         184 :                                                     &fsp->lease->lease.lease_key,
     481             :                                                     break_from, break_to);
     482             :         } else {
     483           0 :                 uint8_t smb2_oplock_level;
     484         242 :                 smb2_oplock_level = (break_to & SMB2_LEASE_READ) ?
     485         242 :                         SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE;
     486         242 :                 status = smbd_smb2_send_oplock_break(client,
     487             :                                                      fsp->op,
     488             :                                                      smb2_oplock_level);
     489             :         }
     490         426 :         if (!NT_STATUS_IS_OK(status)) {
     491           0 :                 smbd_server_disconnect_client(client,
     492             :                                               nt_errstr(status));
     493           0 :                 return;
     494             :         }
     495             : }

Generated by: LCOV version 1.14