tstream: Fix CID 1167981 Unchecked return value
[samba.git] / libcli / smb / tstream_smbXcli_np.c
index 2c2cb4b2f5d8b39338b8af860b90103038edb65e..8dfc4fb2466a1ad1b1879c9a92d64c1a37c93b50 100644 (file)
 
 static const struct tstream_context_ops tstream_smbXcli_np_ops;
 
-/*
- * Windows uses 4280 (the max xmit/recv size negotiated on DCERPC).
- * This is fits into the max_xmit negotiated at the SMB layer.
- *
- * On the sending side they may use SMBtranss if the request does not
- * fit into a single SMBtrans call.
- *
- * Windows uses 1024 as max data size of a SMBtrans request and then
- * possibly reads the rest of the DCERPC fragment (up to 3256 bytes)
- * via a SMBreadX.
- *
- * For now we just ask for the full 4280 bytes (max data size) in the SMBtrans
- * request to get the whole fragment at once (like samba 3.5.x and below did.
- *
- * It is important that we use do SMBwriteX with the size of a full fragment,
- * otherwise we may get NT_STATUS_PIPE_BUSY on the SMBtrans request
- * from NT4 servers. (See bug #8195)
- */
-#define TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE 4280
-
 #define TSTREAM_SMBXCLI_NP_DESIRED_ACCESS ( \
        SEC_STD_READ_CONTROL | \
        SEC_FILE_READ_DATA | \
@@ -63,10 +43,12 @@ static const struct tstream_context_ops tstream_smbXcli_np_ops;
 struct tstream_smbXcli_np_ref;
 
 struct tstream_smbXcli_np {
-       struct tstream_smbXcli_np_ref *ref;
        struct smbXcli_conn *conn;
+       struct tstream_smbXcli_np_ref *conn_ref;
        struct smbXcli_session *session;
+       struct tstream_smbXcli_np_ref *session_ref;
        struct smbXcli_tcon *tcon;
+       struct tstream_smbXcli_np_ref *tcon_ref;
        uint16_t pid;
        unsigned int timeout;
 
@@ -98,9 +80,19 @@ static int tstream_smbXcli_np_destructor(struct tstream_smbXcli_np *cli_nps)
 {
        NTSTATUS status;
 
-       if (cli_nps->ref != NULL) {
-               cli_nps->ref->cli_nps = NULL;
-               TALLOC_FREE(cli_nps->ref);
+       if (cli_nps->conn_ref != NULL) {
+               cli_nps->conn_ref->cli_nps = NULL;
+               TALLOC_FREE(cli_nps->conn_ref);
+       }
+
+       if (cli_nps->session_ref != NULL) {
+               cli_nps->session_ref->cli_nps = NULL;
+               TALLOC_FREE(cli_nps->session_ref);
+       }
+
+       if (cli_nps->tcon_ref != NULL) {
+               cli_nps->tcon_ref->cli_nps = NULL;
+               TALLOC_FREE(cli_nps->tcon_ref);
        }
 
        if (!smbXcli_conn_is_connected(cli_nps->conn)) {
@@ -119,7 +111,11 @@ static int tstream_smbXcli_np_destructor(struct tstream_smbXcli_np *cli_nps)
         * Once we've fixed all callers to call
         * tstream_disconnect_send()/_recv(), this will
         * never be called.
+        *
+        * We use a maximun timeout of 1 second == 1000 msec.
         */
+       cli_nps->timeout = MIN(cli_nps->timeout, 1000);
+
        if (cli_nps->is_smb1) {
                status = smb1cli_close(cli_nps->conn,
                                       cli_nps->timeout,
@@ -153,14 +149,27 @@ static int tstream_smbXcli_np_ref_destructor(struct tstream_smbXcli_np_ref *ref)
                return 0;
        }
 
+       if (ref->cli_nps->conn == NULL) {
+               return 0;
+       }
+
        ref->cli_nps->conn = NULL;
        ref->cli_nps->session = NULL;
        ref->cli_nps->tcon = NULL;
-       ref->cli_nps->ref = NULL;
+
+       TALLOC_FREE(ref->cli_nps->conn_ref);
+       TALLOC_FREE(ref->cli_nps->session_ref);
+       TALLOC_FREE(ref->cli_nps->tcon_ref);
 
        return 0;
 };
 
+static struct tevent_req *tstream_smbXcli_np_disconnect_send(TALLOC_CTX *mem_ctx,
+                                               struct tevent_context *ev,
+                                               struct tstream_context *stream);
+static int tstream_smbXcli_np_disconnect_recv(struct tevent_req *req,
+                                             int *perrno);
+
 struct tstream_smbXcli_np_open_state {
        struct smbXcli_conn *conn;
        struct smbXcli_session *session;
@@ -274,7 +283,7 @@ static void tstream_smbXcli_np_open_done(struct tevent_req *subreq)
                status = smb2cli_create_recv(subreq,
                                             &state->fid_persistent,
                                             &state->fid_volatile,
-                                            NULL);
+                                            NULL, NULL, NULL);
        }
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
@@ -312,13 +321,33 @@ NTSTATUS _tstream_smbXcli_np_open_recv(struct tevent_req *req,
        }
        ZERO_STRUCTP(cli_nps);
 
-       cli_nps->ref = talloc_zero(state->conn, struct tstream_smbXcli_np_ref);
-       if (cli_nps->ref == NULL) {
+       cli_nps->conn_ref = talloc_zero(state->conn,
+                                       struct tstream_smbXcli_np_ref);
+       if (cli_nps->conn_ref == NULL) {
                TALLOC_FREE(cli_nps);
                tevent_req_received(req);
                return NT_STATUS_NO_MEMORY;
        }
-       cli_nps->ref->cli_nps = cli_nps;
+       cli_nps->conn_ref->cli_nps = cli_nps;
+
+       cli_nps->session_ref = talloc_zero(state->session,
+                                       struct tstream_smbXcli_np_ref);
+       if (cli_nps->session_ref == NULL) {
+               TALLOC_FREE(cli_nps);
+               tevent_req_received(req);
+               return NT_STATUS_NO_MEMORY;
+       }
+       cli_nps->session_ref->cli_nps = cli_nps;
+
+       cli_nps->tcon_ref = talloc_zero(state->tcon,
+                                       struct tstream_smbXcli_np_ref);
+       if (cli_nps->tcon_ref == NULL) {
+               TALLOC_FREE(cli_nps);
+               tevent_req_received(req);
+               return NT_STATUS_NO_MEMORY;
+       }
+       cli_nps->tcon_ref->cli_nps = cli_nps;
+
        cli_nps->conn = state->conn;
        cli_nps->session = state->session;
        cli_nps->tcon = state->tcon;
@@ -331,7 +360,12 @@ NTSTATUS _tstream_smbXcli_np_open_recv(struct tevent_req *req,
        cli_nps->fid_volatile = state->fid_volatile;
 
        talloc_set_destructor(cli_nps, tstream_smbXcli_np_destructor);
-       talloc_set_destructor(cli_nps->ref, tstream_smbXcli_np_ref_destructor);
+       talloc_set_destructor(cli_nps->conn_ref,
+                             tstream_smbXcli_np_ref_destructor);
+       talloc_set_destructor(cli_nps->session_ref,
+                             tstream_smbXcli_np_ref_destructor);
+       talloc_set_destructor(cli_nps->tcon_ref,
+                             tstream_smbXcli_np_ref_destructor);
 
        cli_nps->trans.active = false;
        cli_nps->trans.read_req = NULL;
@@ -602,7 +636,7 @@ static void tstream_smbXcli_np_writev_write_done(struct tevent_req *subreq)
        }
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
-               tstream_smbXcli_np_writev_disconnect_now(req, EIO, __location__);
+               tstream_smbXcli_np_writev_disconnect_now(req, EPIPE, __location__);
                return;
        }
 
@@ -637,24 +671,8 @@ static void tstream_smbXcli_np_writev_disconnect_now(struct tevent_req *req,
                return;
        }
 
-       if (cli_nps->is_smb1) {
-               subreq = smb1cli_close_send(state, state->ev,
-                                           cli_nps->conn,
-                                           cli_nps->timeout,
-                                           cli_nps->pid,
-                                           cli_nps->tcon,
-                                           cli_nps->session,
-                                           cli_nps->fnum, UINT32_MAX);
-       } else {
-               subreq = smb2cli_close_send(state, state->ev,
-                                           cli_nps->conn,
-                                           cli_nps->timeout,
-                                           cli_nps->session,
-                                           cli_nps->tcon,
-                                           0, /* flags */
-                                           cli_nps->fid_persistent,
-                                           cli_nps->fid_volatile);
-       }
+       subreq = tstream_smbXcli_np_disconnect_send(state, state->ev,
+                                                   state->stream);
        if (subreq == NULL) {
                /* return the original error */
                _tevent_req_error(req, state->error.val, state->error.location);
@@ -671,20 +689,11 @@ static void tstream_smbXcli_np_writev_disconnect_done(struct tevent_req *subreq)
                tevent_req_callback_data(subreq, struct tevent_req);
        struct tstream_smbXcli_np_writev_state *state =
                tevent_req_data(req, struct tstream_smbXcli_np_writev_state);
-       struct tstream_smbXcli_np *cli_nps =
-               tstream_context_data(state->stream, struct tstream_smbXcli_np);
+       int error;
 
-       if (cli_nps->is_smb1) {
-               smb1cli_close_recv(subreq);
-       } else {
-               smb2cli_close_recv(subreq);
-       }
+       tstream_smbXcli_np_disconnect_recv(subreq, &error);
        TALLOC_FREE(subreq);
 
-       cli_nps->conn = NULL;
-       cli_nps->tcon = NULL;
-       cli_nps->session = NULL;
-
        /* return the original error */
        _tevent_req_error(req, state->error.val, state->error.location);
 }
@@ -971,11 +980,18 @@ static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq)
                received = out_output_buffer.length;
        }
        TALLOC_FREE(subreq);
-       if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
+       if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+               /*
+                * STATUS_BUFFER_OVERFLOW means that there's
+                * more data to read when the named pipe is used
+                * in message mode (which is the case here).
+                *
+                * But we hide this from the caller.
+                */
                status = NT_STATUS_OK;
        }
        if (!NT_STATUS_IS_OK(status)) {
-               tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__);
+               tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
                return;
        }
 
@@ -994,7 +1010,7 @@ static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq)
        cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
        if (cli_nps->read.buf == NULL) {
                TALLOC_FREE(subreq);
-               tevent_req_nomem(cli_nps->read.buf, req);
+               tevent_req_oom(req);
                return;
        }
        memcpy(cli_nps->read.buf, rcvbuf, received);
@@ -1047,9 +1063,9 @@ static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq)
         * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a
         * child of that.
         */
-       if (NT_STATUS_EQUAL(status, NT_STATUS_BUFFER_TOO_SMALL)) {
+       if (NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
                /*
-                * NT_STATUS_BUFFER_TOO_SMALL means that there's
+                * STATUS_BUFFER_OVERFLOW means that there's
                 * more data to read when the named pipe is used
                 * in message mode (which is the case here).
                 *
@@ -1059,7 +1075,7 @@ static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq)
        }
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(subreq);
-               tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__);
+               tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__);
                return;
        }
 
@@ -1080,7 +1096,7 @@ static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq)
        cli_nps->read.buf = talloc_array(cli_nps, uint8_t, received);
        if (cli_nps->read.buf == NULL) {
                TALLOC_FREE(subreq);
-               tevent_req_nomem(cli_nps->read.buf, req);
+               tevent_req_oom(req);
                return;
        }
        memcpy(cli_nps->read.buf, rcvbuf, received);
@@ -1114,24 +1130,8 @@ static void tstream_smbXcli_np_readv_disconnect_now(struct tevent_req *req,
                return;
        }
 
-       if (cli_nps->is_smb1) {
-               subreq = smb1cli_close_send(state, state->ev,
-                                           cli_nps->conn,
-                                           cli_nps->timeout,
-                                           cli_nps->pid,
-                                           cli_nps->tcon,
-                                           cli_nps->session,
-                                           cli_nps->fnum, UINT32_MAX);
-       } else {
-               subreq = smb2cli_close_send(state, state->ev,
-                                           cli_nps->conn,
-                                           cli_nps->timeout,
-                                           cli_nps->session,
-                                           cli_nps->tcon,
-                                           0, /* flags */
-                                           cli_nps->fid_persistent,
-                                           cli_nps->fid_volatile);
-       }
+       subreq = tstream_smbXcli_np_disconnect_send(state, state->ev,
+                                                   state->stream);
        if (subreq == NULL) {
                /* return the original error */
                tstream_smbXcli_np_readv_error(req);
@@ -1146,22 +1146,11 @@ static void tstream_smbXcli_np_readv_disconnect_done(struct tevent_req *subreq)
 {
        struct tevent_req *req =
                tevent_req_callback_data(subreq, struct tevent_req);
-       struct tstream_smbXcli_np_readv_state *state =
-               tevent_req_data(req, struct tstream_smbXcli_np_readv_state);
-       struct tstream_smbXcli_np *cli_nps =
-               tstream_context_data(state->stream, struct tstream_smbXcli_np);
+       int error;
 
-       if (cli_nps->is_smb1) {
-               smb1cli_close_recv(subreq);
-       } else {
-               smb2cli_close_recv(subreq);
-       }
+       tstream_smbXcli_np_disconnect_recv(subreq, &error);
        TALLOC_FREE(subreq);
 
-       cli_nps->conn = NULL;
-       cli_nps->session = NULL;
-       cli_nps->tcon = NULL;
-
        tstream_smbXcli_np_readv_error(req);
 }
 
@@ -1231,9 +1220,12 @@ static int tstream_smbXcli_np_readv_recv(struct tevent_req *req,
 
 struct tstream_smbXcli_np_disconnect_state {
        struct tstream_context *stream;
+       struct tevent_req *subreq;
 };
 
 static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq);
+static void tstream_smbXcli_np_disconnect_cleanup(struct tevent_req *req,
+                                       enum tevent_req_state req_state);
 
 static struct tevent_req *tstream_smbXcli_np_disconnect_send(TALLOC_CTX *mem_ctx,
                                                struct tevent_context *ev,
@@ -1278,6 +1270,14 @@ static struct tevent_req *tstream_smbXcli_np_disconnect_send(TALLOC_CTX *mem_ctx
                return tevent_req_post(req, ev);
        }
        tevent_req_set_callback(subreq, tstream_smbXcli_np_disconnect_done, req);
+       state->subreq = subreq;
+
+       tevent_req_set_cleanup_fn(req, tstream_smbXcli_np_disconnect_cleanup);
+
+       /*
+        * Make sure we don't send any requests anymore.
+        */
+       cli_nps->conn = NULL;
 
        return req;
 }
@@ -1292,6 +1292,8 @@ static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq)
                tstream_context_data(state->stream, struct tstream_smbXcli_np);
        NTSTATUS status;
 
+       state->subreq = NULL;
+
        if (cli_nps->is_smb1) {
                status = smb1cli_close_recv(subreq);
        } else {
@@ -1299,7 +1301,7 @@ static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq)
        }
        TALLOC_FREE(subreq);
        if (!NT_STATUS_IS_OK(status)) {
-               tevent_req_error(req, EIO);
+               tevent_req_error(req, EPIPE);
                return;
        }
 
@@ -1310,6 +1312,51 @@ static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq)
        tevent_req_done(req);
 }
 
+static void tstream_smbXcli_np_disconnect_free(struct tevent_req *subreq);
+
+static void tstream_smbXcli_np_disconnect_cleanup(struct tevent_req *req,
+                                       enum tevent_req_state req_state)
+{
+       struct tstream_smbXcli_np_disconnect_state *state =
+               tevent_req_data(req, struct tstream_smbXcli_np_disconnect_state);
+       struct tstream_smbXcli_np *cli_nps = NULL;
+
+       if (state->subreq == NULL) {
+               return;
+       }
+
+       cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np);
+
+       if (cli_nps->tcon == NULL) {
+               return;
+       }
+
+       /*
+        * We're no longer interested in the result
+        * any more, but need to make sure that the close
+        * request arrives at the server if the smb connection,
+        * session and tcon are still alive.
+        *
+        * We move the low level request to the tcon,
+        * which means that it stays as long as the tcon
+        * is available.
+        */
+       talloc_steal(cli_nps->tcon, state->subreq);
+       tevent_req_set_callback(state->subreq,
+                               tstream_smbXcli_np_disconnect_free,
+                               NULL);
+       state->subreq = NULL;
+
+       cli_nps->conn = NULL;
+       cli_nps->session = NULL;
+       cli_nps->tcon = NULL;
+}
+
+static void tstream_smbXcli_np_disconnect_free(struct tevent_req *subreq)
+{
+       TALLOC_FREE(subreq);
+}
+
 static int tstream_smbXcli_np_disconnect_recv(struct tevent_req *req,
                                              int *perrno)
 {