LCOV - code coverage report
Current view: top level - source3/smbd - smb2_notify.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 103 139 74.1 %
Date: 2024-06-13 04:01:37 Functions: 8 8 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             : 
      28             : #undef DBGC_CLASS
      29             : #define DBGC_CLASS DBGC_SMB2
      30             : 
      31             : struct smbd_smb2_notify_state {
      32             :         struct smbd_smb2_request *smb2req;
      33             :         struct smb_request *smbreq;
      34             :         bool has_request;
      35             :         bool skip_reply;
      36             :         NTSTATUS status;
      37             :         DATA_BLOB out_output_buffer;
      38             : };
      39             : 
      40             : static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
      41             :                                                 struct tevent_context *ev,
      42             :                                                 struct smbd_smb2_request *smb2req,
      43             :                                                 struct files_struct *in_fsp,
      44             :                                                 uint16_t in_flags,
      45             :                                                 uint32_t in_output_buffer_length,
      46             :                                                 uint64_t in_completion_filter);
      47             : static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
      48             :                                       TALLOC_CTX *mem_ctx,
      49             :                                       DATA_BLOB *out_output_buffer);
      50             : 
      51             : static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
      52          60 : NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
      53             : {
      54          60 :         struct smbXsrv_connection *xconn = req->xconn;
      55             :         NTSTATUS status;
      56             :         const uint8_t *inbody;
      57             :         uint16_t in_flags;
      58             :         uint32_t in_output_buffer_length;
      59             :         uint64_t in_file_id_persistent;
      60             :         uint64_t in_file_id_volatile;
      61             :         struct files_struct *in_fsp;
      62             :         uint64_t in_completion_filter;
      63             :         struct tevent_req *subreq;
      64             : 
      65          60 :         status = smbd_smb2_request_verify_sizes(req, 0x20);
      66          60 :         if (!NT_STATUS_IS_OK(status)) {
      67           0 :                 return smbd_smb2_request_error(req, status);
      68             :         }
      69          60 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      70             : 
      71          60 :         in_flags                = SVAL(inbody, 0x02);
      72          60 :         in_output_buffer_length = IVAL(inbody, 0x04);
      73          60 :         in_file_id_persistent   = BVAL(inbody, 0x08);
      74          60 :         in_file_id_volatile     = BVAL(inbody, 0x10);
      75          60 :         in_completion_filter    = IVAL(inbody, 0x18);
      76             : 
      77             :         /*
      78             :          * 0x00010000 is what Windows 7 uses,
      79             :          * Windows 2008 uses 0x00080000
      80             :          */
      81          60 :         if (in_output_buffer_length > xconn->smb2.server.max_trans) {
      82           0 :                 return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
      83             :         }
      84             : 
      85          60 :         status = smbd_smb2_request_verify_creditcharge(req,
      86             :                                                 in_output_buffer_length);
      87             : 
      88          60 :         if (!NT_STATUS_IS_OK(status)) {
      89           0 :                 return smbd_smb2_request_error(req, status);
      90             :         }
      91             : 
      92          60 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
      93          60 :         if (in_fsp == NULL) {
      94           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
      95             :         }
      96             : 
      97          60 :         subreq = smbd_smb2_notify_send(req, req->sconn->ev_ctx,
      98             :                                        req, in_fsp,
      99             :                                        in_flags,
     100             :                                        in_output_buffer_length,
     101             :                                        in_completion_filter);
     102          60 :         if (subreq == NULL) {
     103           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     104             :         }
     105          60 :         tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
     106             : 
     107          60 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     108             : }
     109             : 
     110          60 : static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
     111             : {
     112          60 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     113             :                                         struct smbd_smb2_request);
     114             :         DATA_BLOB outbody;
     115             :         DATA_BLOB outdyn;
     116             :         uint16_t out_output_buffer_offset;
     117          60 :         DATA_BLOB out_output_buffer = data_blob_null;
     118             :         NTSTATUS status;
     119             :         NTSTATUS error; /* transport error */
     120             : 
     121          60 :         status = smbd_smb2_notify_recv(subreq,
     122             :                                        req,
     123             :                                        &out_output_buffer);
     124          60 :         TALLOC_FREE(subreq);
     125          60 :         if (!NT_STATUS_IS_OK(status)) {
     126          30 :                 error = smbd_smb2_request_error(req, status);
     127          30 :                 if (!NT_STATUS_IS_OK(error)) {
     128           0 :                         smbd_server_connection_terminate(req->xconn,
     129             :                                                          nt_errstr(error));
     130          15 :                         return;
     131             :                 }
     132          30 :                 return;
     133             :         }
     134             : 
     135          30 :         out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
     136             : 
     137          30 :         outbody = smbd_smb2_generate_outbody(req, 0x08);
     138          30 :         if (outbody.data == NULL) {
     139           0 :                 error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     140           0 :                 if (!NT_STATUS_IS_OK(error)) {
     141           0 :                         smbd_server_connection_terminate(req->xconn,
     142             :                                                          nt_errstr(error));
     143           0 :                         return;
     144             :                 }
     145           0 :                 return;
     146             :         }
     147             : 
     148          30 :         SSVAL(outbody.data, 0x00, 0x08 + 1);    /* struct size */
     149          30 :         SSVAL(outbody.data, 0x02,
     150             :               out_output_buffer_offset);        /* output buffer offset */
     151          30 :         SIVAL(outbody.data, 0x04,
     152             :               out_output_buffer.length);        /* output buffer length */
     153             : 
     154          30 :         outdyn = out_output_buffer;
     155             : 
     156          30 :         error = smbd_smb2_request_done(req, outbody, &outdyn);
     157          30 :         if (!NT_STATUS_IS_OK(error)) {
     158           0 :                 smbd_server_connection_terminate(req->xconn,
     159             :                                                  nt_errstr(error));
     160           0 :                 return;
     161             :         }
     162             : }
     163             : 
     164             : static void smbd_smb2_notify_reply(struct smb_request *smbreq,
     165             :                                    NTSTATUS error_code,
     166             :                                    uint8_t *buf, size_t len);
     167             : static bool smbd_smb2_notify_cancel(struct tevent_req *req);
     168             : 
     169          60 : static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
     170             : {
     171          60 :         if (!state->has_request) {
     172          60 :                 return 0;
     173             :         }
     174             : 
     175           0 :         state->skip_reply = true;
     176           0 :         smbd_notify_cancel_by_smbreq(state->smbreq);
     177           0 :         return 0;
     178             : }
     179             : 
     180          60 : static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
     181             : {
     182          60 :         struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
     183             :                                                        struct tevent_req);
     184          60 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     185             :                                                struct smbd_smb2_notify_state);
     186             : 
     187             :         /*
     188             :          * Our temporary parent from change_notify_add_request()
     189             :          * goes away.
     190             :          */
     191          60 :         state->has_request = false;
     192             : 
     193             :         /*
     194             :          * move it back to its original parent,
     195             :          * which means we no longer need the destructor
     196             :          * to protect it.
     197             :          */
     198          60 :         talloc_steal(smbreq->smb2req, smbreq);
     199          60 :         talloc_set_destructor(smbreq, NULL);
     200             : 
     201             :         /*
     202             :          * We want to keep smbreq!
     203             :          */
     204          60 :         return -1;
     205             : }
     206             : 
     207          60 : static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
     208             :                                                 struct tevent_context *ev,
     209             :                                                 struct smbd_smb2_request *smb2req,
     210             :                                                 struct files_struct *fsp,
     211             :                                                 uint16_t in_flags,
     212             :                                                 uint32_t in_output_buffer_length,
     213             :                                                 uint64_t in_completion_filter)
     214             : {
     215             :         struct tevent_req *req;
     216             :         struct smbd_smb2_notify_state *state;
     217             :         struct smb_request *smbreq;
     218          60 :         connection_struct *conn = smb2req->tcon->compat;
     219          60 :         bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
     220             :         NTSTATUS status;
     221             : 
     222          60 :         req = tevent_req_create(mem_ctx, &state,
     223             :                                 struct smbd_smb2_notify_state);
     224          60 :         if (req == NULL) {
     225           0 :                 return NULL;
     226             :         }
     227          60 :         state->smb2req = smb2req;
     228          60 :         state->status = NT_STATUS_INTERNAL_ERROR;
     229          60 :         state->out_output_buffer = data_blob_null;
     230          60 :         talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
     231             : 
     232          60 :         DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
     233             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
     234             : 
     235          60 :         smbreq = smbd_smb2_fake_smb_request(smb2req);
     236          60 :         if (tevent_req_nomem(smbreq, req)) {
     237           0 :                 return tevent_req_post(req, ev);
     238             :         }
     239             : 
     240          60 :         state->smbreq = smbreq;
     241          60 :         smbreq->async_priv = (void *)req;
     242             : 
     243          60 :         if (DEBUGLEVEL >= 3) {
     244             :                 char *filter_string;
     245             : 
     246           0 :                 filter_string = notify_filter_string(NULL, in_completion_filter);
     247           0 :                 if (tevent_req_nomem(filter_string, req)) {
     248           0 :                         return tevent_req_post(req, ev);
     249             :                 }
     250             : 
     251           0 :                 DEBUG(3,("smbd_smb2_notify_send: notify change "
     252             :                          "called on %s, filter = %s, recursive = %d\n",
     253             :                          fsp_str_dbg(fsp), filter_string, recursive));
     254             : 
     255           0 :                 TALLOC_FREE(filter_string);
     256             :         }
     257             : 
     258          60 :         if ((!fsp->fsp_flags.is_directory) || (conn != fsp->conn)) {
     259           0 :                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
     260           0 :                 return tevent_req_post(req, ev);
     261             :         }
     262             : 
     263          60 :         if (fsp->notify == NULL) {
     264             : 
     265          30 :                 status = change_notify_create(fsp,
     266             :                                               in_output_buffer_length,
     267             :                                               in_completion_filter,
     268             :                                               recursive);
     269          30 :                 if (!NT_STATUS_IS_OK(status)) {
     270           0 :                         DEBUG(10, ("change_notify_create returned %s\n",
     271             :                                    nt_errstr(status)));
     272           0 :                         tevent_req_nterror(req, status);
     273           0 :                         return tevent_req_post(req, ev);
     274             :                 }
     275             :         }
     276             : 
     277          60 :         if (change_notify_fsp_has_changes(fsp)) {
     278             : 
     279             :                 /*
     280             :                  * We've got changes pending, respond immediately
     281             :                  */
     282             : 
     283             :                 /*
     284             :                  * TODO: write a torture test to check the filtering behaviour
     285             :                  * here.
     286             :                  */
     287             : 
     288           0 :                 change_notify_reply(smbreq,
     289           0 :                                     NT_STATUS_OK,
     290             :                                     in_output_buffer_length,
     291             :                                     fsp->notify,
     292             :                                     smbd_smb2_notify_reply);
     293             : 
     294             :                 /*
     295             :                  * change_notify_reply() above has independently
     296             :                  * called tevent_req_done().
     297             :                  */
     298           0 :                 return tevent_req_post(req, ev);
     299             :         }
     300             : 
     301             :         /*
     302             :          * No changes pending, queue the request
     303             :          */
     304             : 
     305          60 :         status = change_notify_add_request(smbreq,
     306             :                         in_output_buffer_length,
     307             :                         in_completion_filter,
     308             :                         recursive, fsp,
     309             :                         smbd_smb2_notify_reply);
     310          60 :         if (!NT_STATUS_IS_OK(status)) {
     311           0 :                 tevent_req_nterror(req, status);
     312           0 :                 return tevent_req_post(req, ev);
     313             :         }
     314             : 
     315             :         /*
     316             :          * This is a HACK!
     317             :          *
     318             :          * change_notify_add_request() talloc_moves()
     319             :          * smbreq away from us, so we need a destructor
     320             :          * which moves it back at the end.
     321             :          */
     322          60 :         state->has_request = true;
     323          60 :         talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
     324             : 
     325             :         /* allow this request to be canceled */
     326          60 :         tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
     327             : 
     328          60 :         SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
     329          60 :         return req;
     330             : }
     331             : 
     332          60 : static void smbd_smb2_notify_reply(struct smb_request *smbreq,
     333             :                                    NTSTATUS error_code,
     334             :                                    uint8_t *buf, size_t len)
     335             : {
     336          60 :         struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
     337             :                                                        struct tevent_req);
     338          60 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     339             :                                                struct smbd_smb2_notify_state);
     340             : 
     341          60 :         if (state->skip_reply) {
     342           0 :                 return;
     343             :         }
     344             : 
     345          60 :         SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
     346             : 
     347          60 :         state->status = error_code;
     348          60 :         if (!NT_STATUS_IS_OK(error_code)) {
     349             :                 /* nothing */
     350          30 :         } else if (len == 0) {
     351           0 :                 state->status = NT_STATUS_NOTIFY_ENUM_DIR;
     352             :         } else {
     353          30 :                 state->out_output_buffer = data_blob_talloc(state, buf, len);
     354          30 :                 if (state->out_output_buffer.data == NULL) {
     355           0 :                         state->status = NT_STATUS_NO_MEMORY;
     356             :                 }
     357             :         }
     358             : 
     359          60 :         tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
     360             : 
     361          60 :         if (!NT_STATUS_IS_OK(state->status)) {
     362          30 :                 tevent_req_nterror(req, state->status);
     363          30 :                 return;
     364             :         }
     365             : 
     366          30 :         tevent_req_done(req);
     367             : }
     368             : 
     369          18 : static bool smbd_smb2_notify_cancel(struct tevent_req *req)
     370             : {
     371          18 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     372             :                                                struct smbd_smb2_notify_state);
     373             : 
     374          18 :         smbd_notify_cancel_by_smbreq(state->smbreq);
     375             : 
     376          18 :         return true;
     377             : }
     378             : 
     379          60 : static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
     380             :                                       TALLOC_CTX *mem_ctx,
     381             :                                       DATA_BLOB *out_output_buffer)
     382             : {
     383             :         NTSTATUS status;
     384          60 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     385             :                                                struct smbd_smb2_notify_state);
     386             : 
     387          60 :         if (tevent_req_is_nterror(req, &status)) {
     388          30 :                 tevent_req_received(req);
     389          30 :                 return status;
     390             :         }
     391             : 
     392          30 :         *out_output_buffer = state->out_output_buffer;
     393          30 :         talloc_steal(mem_ctx, out_output_buffer->data);
     394             : 
     395          30 :         tevent_req_received(req);
     396          30 :         return NT_STATUS_OK;
     397             : }

Generated by: LCOV version 1.13