LCOV - code coverage report
Current view: top level - source3/smbd - smb2_notify.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 129 156 82.7 %
Date: 2024-04-21 15:09:00 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        1738 : NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
      53             : {
      54        1738 :         struct smbXsrv_connection *xconn = req->xconn;
      55          16 :         NTSTATUS status;
      56          16 :         const uint8_t *inbody;
      57          16 :         uint16_t in_flags;
      58          16 :         uint32_t in_output_buffer_length;
      59          16 :         uint64_t in_file_id_persistent;
      60          16 :         uint64_t in_file_id_volatile;
      61          16 :         struct files_struct *in_fsp;
      62          16 :         uint64_t in_completion_filter;
      63          16 :         struct tevent_req *subreq;
      64             : 
      65        1738 :         status = smbd_smb2_request_verify_sizes(req, 0x20);
      66        1738 :         if (!NT_STATUS_IS_OK(status)) {
      67           0 :                 return smbd_smb2_request_error(req, status);
      68             :         }
      69        1738 :         inbody = SMBD_SMB2_IN_BODY_PTR(req);
      70             : 
      71        1738 :         in_flags                = SVAL(inbody, 0x02);
      72        1738 :         in_output_buffer_length = IVAL(inbody, 0x04);
      73        1738 :         in_file_id_persistent   = BVAL(inbody, 0x08);
      74        1738 :         in_file_id_volatile     = BVAL(inbody, 0x10);
      75        1738 :         in_completion_filter    = IVAL(inbody, 0x18);
      76             : 
      77             :         /*
      78             :          * 0x00010000 is what Windows 7 uses,
      79             :          * Windows 2008 uses 0x00080000
      80             :          */
      81        1738 :         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        1738 :         status = smbd_smb2_request_verify_creditcharge(req,
      86             :                                                 in_output_buffer_length);
      87             : 
      88        1738 :         if (!NT_STATUS_IS_OK(status)) {
      89           0 :                 return smbd_smb2_request_error(req, status);
      90             :         }
      91             : 
      92        1738 :         in_fsp = file_fsp_smb2(req, in_file_id_persistent, in_file_id_volatile);
      93        1738 :         if (in_fsp == NULL) {
      94           0 :                 return smbd_smb2_request_error(req, NT_STATUS_FILE_CLOSED);
      95             :         }
      96             : 
      97        1738 :         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        1738 :         if (subreq == NULL) {
     103           0 :                 return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
     104             :         }
     105        1738 :         tevent_req_set_callback(subreq, smbd_smb2_request_notify_done, req);
     106             : 
     107        1738 :         return smbd_smb2_request_pending_queue(req, subreq, 500);
     108             : }
     109             : 
     110        1734 : static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
     111             : {
     112        1734 :         struct smbd_smb2_request *req = tevent_req_callback_data(subreq,
     113             :                                         struct smbd_smb2_request);
     114          16 :         DATA_BLOB outbody;
     115          16 :         DATA_BLOB outdyn;
     116          16 :         uint16_t out_output_buffer_offset;
     117        1734 :         DATA_BLOB out_output_buffer = data_blob_null;
     118          16 :         NTSTATUS status;
     119          16 :         NTSTATUS error; /* transport error */
     120             : 
     121        1734 :         status = smbd_smb2_notify_recv(subreq,
     122             :                                        req,
     123             :                                        &out_output_buffer);
     124        1734 :         TALLOC_FREE(subreq);
     125        1734 :         if (!NT_STATUS_IS_OK(status)) {
     126        1612 :                 error = smbd_smb2_request_error(req, status);
     127        1612 :                 if (!NT_STATUS_IS_OK(error)) {
     128           0 :                         smbd_server_connection_terminate(req->xconn,
     129             :                                                          nt_errstr(error));
     130        1612 :                         return;
     131             :                 }
     132        1596 :                 return;
     133             :         }
     134             : 
     135         122 :         out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
     136             : 
     137         122 :         outbody = smbd_smb2_generate_outbody(req, 0x08);
     138         122 :         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         122 :         SSVAL(outbody.data, 0x00, 0x08 + 1);    /* struct size */
     149         122 :         SSVAL(outbody.data, 0x02,
     150             :               out_output_buffer_offset);        /* output buffer offset */
     151         122 :         SIVAL(outbody.data, 0x04,
     152             :               out_output_buffer.length);        /* output buffer length */
     153             : 
     154         122 :         outdyn = out_output_buffer;
     155             : 
     156         122 :         error = smbd_smb2_request_done(req, outbody, &outdyn);
     157         122 :         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        1738 : static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
     170             : {
     171        1738 :         if (!state->has_request) {
     172        1722 :                 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        1678 : static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
     181             : {
     182        1678 :         struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
     183             :                                                        struct tevent_req);
     184        1678 :         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        1678 :         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        1678 :         talloc_steal(smbreq->smb2req, smbreq);
     199        1678 :         talloc_set_destructor(smbreq, NULL);
     200             : 
     201             :         /*
     202             :          * We want to keep smbreq!
     203             :          */
     204        1678 :         return -1;
     205             : }
     206             : 
     207        1738 : 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          16 :         struct tevent_req *req;
     216          16 :         struct smbd_smb2_notify_state *state;
     217          16 :         struct smb_request *smbreq;
     218        1738 :         connection_struct *conn = smb2req->tcon->compat;
     219        1738 :         bool recursive = (in_flags & SMB2_WATCH_TREE) ? true : false;
     220          16 :         NTSTATUS status;
     221             : 
     222        1738 :         req = tevent_req_create(mem_ctx, &state,
     223             :                                 struct smbd_smb2_notify_state);
     224        1738 :         if (req == NULL) {
     225           0 :                 return NULL;
     226             :         }
     227        1738 :         state->smb2req = smb2req;
     228        1738 :         state->status = NT_STATUS_INTERNAL_ERROR;
     229        1738 :         state->out_output_buffer = data_blob_null;
     230        1738 :         talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
     231             : 
     232        1738 :         DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
     233             :                   fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
     234             : 
     235        1738 :         smbreq = smbd_smb2_fake_smb_request(smb2req, fsp);
     236        1738 :         if (tevent_req_nomem(smbreq, req)) {
     237           0 :                 return tevent_req_post(req, ev);
     238             :         }
     239             : 
     240        1738 :         state->smbreq = smbreq;
     241        1738 :         smbreq->async_priv = (void *)req;
     242             : 
     243        1738 :         if (DEBUGLEVEL >= 3) {
     244           0 :                 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        1738 :         if ((!fsp->fsp_flags.is_directory) || (conn != fsp->conn)) {
     259           2 :                 tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
     260           2 :                 return tevent_req_post(req, ev);
     261             :         }
     262             : 
     263        1736 :         if (fsp->notify == NULL) {
     264             : 
     265         906 :                 status = change_notify_create(fsp,
     266             :                                               in_output_buffer_length,
     267             :                                               in_completion_filter,
     268             :                                               recursive);
     269         906 :                 if (tevent_req_nterror(req, status)) {
     270           4 :                         DEBUG(10, ("change_notify_create returned %s\n",
     271             :                                    nt_errstr(status)));
     272           4 :                         return tevent_req_post(req, ev);
     273             :                 }
     274             :         }
     275             : 
     276        1732 :         if (change_notify_fsp_has_changes(fsp)) {
     277             : 
     278             :                 /*
     279             :                  * We've got changes pending, respond immediately
     280             :                  */
     281             : 
     282             :                 /*
     283             :                  * TODO: write a torture test to check the filtering behaviour
     284             :                  * here.
     285             :                  */
     286             : 
     287          54 :                 change_notify_reply(smbreq,
     288          54 :                                     NT_STATUS_OK,
     289             :                                     in_output_buffer_length,
     290             :                                     fsp->notify,
     291             :                                     smbd_smb2_notify_reply);
     292             : 
     293             :                 /*
     294             :                  * change_notify_reply() above has independently
     295             :                  * called tevent_req_done().
     296             :                  */
     297          54 :                 return tevent_req_post(req, ev);
     298             :         }
     299             : 
     300             :         /*
     301             :          * No changes pending, queue the request
     302             :          */
     303             : 
     304        1678 :         status = change_notify_add_request(smbreq,
     305             :                         in_output_buffer_length,
     306             :                         in_completion_filter,
     307             :                         recursive, fsp,
     308             :                         smbd_smb2_notify_reply);
     309        1678 :         if (tevent_req_nterror(req, status)) {
     310           0 :                 return tevent_req_post(req, ev);
     311             :         }
     312             : 
     313             :         /*
     314             :          * This is a HACK!
     315             :          *
     316             :          * change_notify_add_request() talloc_moves()
     317             :          * smbreq away from us, so we need a destructor
     318             :          * which moves it back at the end.
     319             :          */
     320        1678 :         state->has_request = true;
     321        1678 :         talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
     322             : 
     323             :         /* allow this request to be canceled */
     324        1678 :         tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
     325             : 
     326        1678 :         SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
     327        1662 :         return req;
     328             : }
     329             : 
     330        1732 : static void smbd_smb2_notify_reply(struct smb_request *smbreq,
     331             :                                    NTSTATUS error_code,
     332             :                                    uint8_t *buf, size_t len)
     333             : {
     334        1732 :         struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
     335             :                                                        struct tevent_req);
     336        1732 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     337             :                                                struct smbd_smb2_notify_state);
     338             : 
     339        1732 :         if (state->skip_reply) {
     340           0 :                 return;
     341             :         }
     342             : 
     343        1732 :         SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
     344             : 
     345        1732 :         state->status = error_code;
     346        1732 :         if (!NT_STATUS_IS_OK(error_code)) {
     347             :                 /* nothing */
     348         126 :         } else if (len == 0) {
     349           4 :                 state->status = NT_STATUS_NOTIFY_ENUM_DIR;
     350             :         } else {
     351         122 :                 state->out_output_buffer = data_blob_talloc(state, buf, len);
     352         122 :                 if (state->out_output_buffer.data == NULL) {
     353           0 :                         state->status = NT_STATUS_NO_MEMORY;
     354             :                 }
     355             :         }
     356             : 
     357        1732 :         tevent_req_defer_callback(req, state->smb2req->sconn->ev_ctx);
     358             : 
     359        1732 :         if (tevent_req_nterror(req, state->status)) {
     360        1594 :                 return;
     361             :         }
     362             : 
     363         122 :         tevent_req_done(req);
     364             : }
     365             : 
     366        1578 : static bool smbd_smb2_notify_cancel(struct tevent_req *req)
     367             : {
     368        1578 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     369             :                                                struct smbd_smb2_notify_state);
     370             : 
     371        1578 :         smbd_notify_cancel_by_smbreq(state->smbreq);
     372             : 
     373        1578 :         return true;
     374             : }
     375             : 
     376        1734 : static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
     377             :                                       TALLOC_CTX *mem_ctx,
     378             :                                       DATA_BLOB *out_output_buffer)
     379             : {
     380          16 :         NTSTATUS status;
     381        1734 :         struct smbd_smb2_notify_state *state = tevent_req_data(req,
     382             :                                                struct smbd_smb2_notify_state);
     383             : 
     384        1734 :         if (tevent_req_is_nterror(req, &status)) {
     385        1612 :                 tevent_req_received(req);
     386        1612 :                 return status;
     387             :         }
     388             : 
     389         122 :         *out_output_buffer = state->out_output_buffer;
     390         122 :         talloc_steal(mem_ctx, out_output_buffer->data);
     391             : 
     392         122 :         tevent_req_received(req);
     393         122 :         return NT_STATUS_OK;
     394             : }

Generated by: LCOV version 1.14