s3:smbd: fix max_buffer handling of initial notify requests
[samba.git] / source3 / smbd / smb2_notify.c
index 81aa6152cc5b9a44fad8bdcc9881b825fad87fc2..68429b7b766a324f61ac45047aec6d238a81a2e7 100644 (file)
 #include "../libcli/smb/smb_common.h"
 #include "../lib/util/tevent_ntstatus.h"
 
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_SMB2
+
 struct smbd_smb2_notify_state {
        struct smbd_smb2_request *smb2req;
        struct smb_request *smbreq;
+       bool has_request;
+       bool skip_reply;
        NTSTATUS status;
        DATA_BLOB out_output_buffer;
 };
@@ -46,6 +51,7 @@ static NTSTATUS smbd_smb2_notify_recv(struct tevent_req *req,
 static void smbd_smb2_request_notify_done(struct tevent_req *subreq);
 NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
 {
+       struct smbXsrv_connection *xconn = req->xconn;
        NTSTATUS status;
        const uint8_t *inbody;
        uint16_t in_flags;
@@ -72,7 +78,7 @@ NTSTATUS smbd_smb2_request_process_notify(struct smbd_smb2_request *req)
         * 0x00010000 is what Windows 7 uses,
         * Windows 2008 uses 0x00080000
         */
-       if (in_output_buffer_length > req->sconn->smb2.max_trans) {
+       if (in_output_buffer_length > xconn->smb2.server.max_trans) {
                return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER);
        }
 
@@ -119,7 +125,7 @@ static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
        if (!NT_STATUS_IS_OK(status)) {
                error = smbd_smb2_request_error(req, status);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->sconn,
+                       smbd_server_connection_terminate(req->xconn,
                                                         nt_errstr(error));
                        return;
                }
@@ -128,11 +134,11 @@ static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
 
        out_output_buffer_offset = SMB2_HDR_BODY + 0x08;
 
-       outbody = data_blob_talloc(req->out.vector, NULL, 0x08);
+       outbody = smbd_smb2_generate_outbody(req, 0x08);
        if (outbody.data == NULL) {
                error = smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY);
                if (!NT_STATUS_IS_OK(error)) {
-                       smbd_server_connection_terminate(req->sconn,
+                       smbd_server_connection_terminate(req->xconn,
                                                         nt_errstr(error));
                        return;
                }
@@ -149,7 +155,7 @@ static void smbd_smb2_request_notify_done(struct tevent_req *subreq)
 
        error = smbd_smb2_request_done(req, outbody, &outdyn);
        if (!NT_STATUS_IS_OK(error)) {
-               smbd_server_connection_terminate(req->sconn,
+               smbd_server_connection_terminate(req->xconn,
                                                 nt_errstr(error));
                return;
        }
@@ -160,6 +166,44 @@ static void smbd_smb2_notify_reply(struct smb_request *smbreq,
                                   uint8_t *buf, size_t len);
 static bool smbd_smb2_notify_cancel(struct tevent_req *req);
 
+static int smbd_smb2_notify_state_destructor(struct smbd_smb2_notify_state *state)
+{
+       if (!state->has_request) {
+               return 0;
+       }
+
+       state->skip_reply = true;
+       smbd_notify_cancel_by_smbreq(state->smbreq);
+       return 0;
+}
+
+static int smbd_smb2_notify_smbreq_destructor(struct smb_request *smbreq)
+{
+       struct tevent_req *req = talloc_get_type_abort(smbreq->async_priv,
+                                                      struct tevent_req);
+       struct smbd_smb2_notify_state *state = tevent_req_data(req,
+                                              struct smbd_smb2_notify_state);
+
+       /*
+        * Our temporary parent from change_notify_add_request()
+        * goes away.
+        */
+       state->has_request = false;
+
+       /*
+        * move it back to its original parent,
+        * which means we no longer need the destructor
+        * to protect it.
+        */
+       talloc_steal(smbreq->smb2req, smbreq);
+       talloc_set_destructor(smbreq, NULL);
+
+       /*
+        * We want to keep smbreq!
+        */
+       return -1;
+}
+
 static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
                                                struct tevent_context *ev,
                                                struct smbd_smb2_request *smb2req,
@@ -183,6 +227,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
        state->smb2req = smb2req;
        state->status = NT_STATUS_INTERNAL_ERROR;
        state->out_output_buffer = data_blob_null;
+       talloc_set_destructor(state, smbd_smb2_notify_state_destructor);
 
        DEBUG(10,("smbd_smb2_notify_send: %s - %s\n",
                  fsp_str_dbg(fsp), fsp_fnum_dbg(fsp)));
@@ -195,7 +240,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
        state->smbreq = smbreq;
        smbreq->async_priv = (void *)req;
 
-       {
+       if (DEBUGLEVEL >= 3) {
                char *filter_string;
 
                filter_string = notify_filter_string(NULL, in_completion_filter);
@@ -218,6 +263,7 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
        if (fsp->notify == NULL) {
 
                status = change_notify_create(fsp,
+                                             in_output_buffer_length,
                                              in_completion_filter,
                                              recursive);
                if (!NT_STATUS_IS_OK(status)) {
@@ -266,9 +312,20 @@ static struct tevent_req *smbd_smb2_notify_send(TALLOC_CTX *mem_ctx,
                return tevent_req_post(req, ev);
        }
 
+       /*
+        * This is a HACK!
+        *
+        * change_notify_add_request() talloc_moves()
+        * smbreq away from us, so we need a destructor
+        * which moves it back at the end.
+        */
+       state->has_request = true;
+       talloc_set_destructor(smbreq, smbd_smb2_notify_smbreq_destructor);
+
        /* allow this request to be canceled */
        tevent_req_set_cancel_fn(req, smbd_smb2_notify_cancel);
 
+       SMBPROFILE_IOBYTES_ASYNC_SET_IDLE(state->smb2req->profile);
        return req;
 }
 
@@ -281,6 +338,12 @@ static void smbd_smb2_notify_reply(struct smb_request *smbreq,
        struct smbd_smb2_notify_state *state = tevent_req_data(req,
                                               struct smbd_smb2_notify_state);
 
+       if (state->skip_reply) {
+               return;
+       }
+
+       SMBPROFILE_IOBYTES_ASYNC_SET_BUSY(state->smb2req->profile);
+
        state->status = error_code;
        if (!NT_STATUS_IS_OK(error_code)) {
                /* nothing */