/* Unix SMB/CIFS implementation. Copyright (C) Stefan Metzmacher 2010 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "system/network.h" #include "../lib/util/tevent_ntstatus.h" #include "../lib/tsocket/tsocket.h" #include "../lib/tsocket/tsocket_internal.h" #include "smb_common.h" #include "smbXcli_base.h" #include "tstream_smbXcli_np.h" #include "libcli/security/security.h" static const struct tstream_context_ops tstream_smbXcli_np_ops; #define TSTREAM_SMBXCLI_NP_DESIRED_ACCESS ( \ SEC_STD_READ_CONTROL | \ SEC_FILE_READ_DATA | \ SEC_FILE_WRITE_DATA | \ SEC_FILE_APPEND_DATA | \ SEC_FILE_READ_EA | \ SEC_FILE_WRITE_EA | \ SEC_FILE_READ_ATTRIBUTE | \ SEC_FILE_WRITE_ATTRIBUTE | \ 0) struct tstream_smbXcli_np_ref; struct tstream_smbXcli_np { 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; const char *npipe; bool is_smb1; uint16_t fnum; uint64_t fid_persistent; uint64_t fid_volatile; struct { bool active; struct tevent_req *read_req; struct tevent_req *write_req; uint16_t setup[2]; } trans; struct { off_t ofs; size_t left; uint8_t *buf; } read, write; }; struct tstream_smbXcli_np_ref { struct tstream_smbXcli_np *cli_nps; }; static int tstream_smbXcli_np_destructor(struct tstream_smbXcli_np *cli_nps) { NTSTATUS status; 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)) { return 0; } /* * TODO: do not use a sync call with a destructor!!! * * This only happens, if a caller does talloc_free(), * while the everything was still ok. * * If we get an unexpected failure within a normal * operation, we already do an async cli_close_send()/_recv(). * * 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, cli_nps->pid, cli_nps->tcon, cli_nps->session, cli_nps->fnum, UINT32_MAX); } else { status = smb2cli_close(cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, 0, /* flags */ cli_nps->fid_persistent, cli_nps->fid_volatile); } if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("tstream_smbXcli_np_destructor: cli_close " "failed on pipe %s. Error was %s\n", cli_nps->npipe, nt_errstr(status))); } /* * We can't do much on failure */ return 0; } static int tstream_smbXcli_np_ref_destructor(struct tstream_smbXcli_np_ref *ref) { if (ref->cli_nps == NULL) { 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; 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; struct smbXcli_tcon *tcon; uint16_t pid; unsigned int timeout; bool is_smb1; uint16_t fnum; uint64_t fid_persistent; uint64_t fid_volatile; const char *npipe; }; static void tstream_smbXcli_np_open_done(struct tevent_req *subreq); struct tevent_req *tstream_smbXcli_np_open_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct smbXcli_conn *conn, struct smbXcli_session *session, struct smbXcli_tcon *tcon, uint16_t pid, unsigned int timeout, const char *npipe) { struct tevent_req *req; struct tstream_smbXcli_np_open_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tstream_smbXcli_np_open_state); if (!req) { return NULL; } state->conn = conn; state->tcon = tcon; state->session = session; state->pid = pid; state->timeout = timeout; state->npipe = talloc_strdup(state, npipe); if (tevent_req_nomem(state->npipe, req)) { return tevent_req_post(req, ev); } if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) { state->is_smb1 = true; } if (state->is_smb1) { const char *smb1_npipe; /* * Windows and newer Samba versions allow * the pipe name without leading backslash, * but we should better behave like windows clients */ smb1_npipe = talloc_asprintf(state, "\\%s", state->npipe); if (tevent_req_nomem(smb1_npipe, req)) { return tevent_req_post(req, ev); } subreq = smb1cli_ntcreatex_send(state, ev, state->conn, state->timeout, state->pid, state->tcon, state->session, smb1_npipe, 0, /* CreatFlags */ 0, /* RootDirectoryFid */ TSTREAM_SMBXCLI_NP_DESIRED_ACCESS, 0, /* AllocationSize */ 0, /* FileAttributes */ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, /* CreateDisposition */ 0, /* CreateOptions */ 2, /* NTCREATEX_IMPERSONATION_IMPERSONATION */ 0); /* SecurityFlags */ } else { subreq = smb2cli_create_send(state, ev, state->conn, state->timeout, state->session, state->tcon, npipe, SMB2_OPLOCK_LEVEL_NONE, SMB2_IMPERSONATION_IMPERSONATION, TSTREAM_SMBXCLI_NP_DESIRED_ACCESS, 0, /* file_attributes */ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, /* create_options */ NULL); /* blobs */ } if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); } tevent_req_set_callback(subreq, tstream_smbXcli_np_open_done, req); return req; } static void tstream_smbXcli_np_open_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tstream_smbXcli_np_open_state *state = tevent_req_data(req, struct tstream_smbXcli_np_open_state); NTSTATUS status; if (state->is_smb1) { status = smb1cli_ntcreatex_recv(subreq, &state->fnum); } else { status = smb2cli_create_recv(subreq, &state->fid_persistent, &state->fid_volatile, NULL, NULL, NULL); } TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(req, status); return; } tevent_req_done(req); } NTSTATUS _tstream_smbXcli_np_open_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, struct tstream_context **_stream, const char *location) { struct tstream_smbXcli_np_open_state *state = tevent_req_data(req, struct tstream_smbXcli_np_open_state); struct tstream_context *stream; struct tstream_smbXcli_np *cli_nps; NTSTATUS status; if (tevent_req_is_nterror(req, &status)) { tevent_req_received(req); return status; } stream = tstream_context_create(mem_ctx, &tstream_smbXcli_np_ops, &cli_nps, struct tstream_smbXcli_np, location); if (!stream) { tevent_req_received(req); return NT_STATUS_NO_MEMORY; } ZERO_STRUCTP(cli_nps); 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->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; cli_nps->pid = state->pid; cli_nps->timeout = state->timeout; cli_nps->npipe = talloc_move(cli_nps, &state->npipe); cli_nps->is_smb1 = state->is_smb1; cli_nps->fnum = state->fnum; cli_nps->fid_persistent = state->fid_persistent; cli_nps->fid_volatile = state->fid_volatile; talloc_set_destructor(cli_nps, tstream_smbXcli_np_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; cli_nps->trans.write_req = NULL; SSVAL(cli_nps->trans.setup+0, 0, TRANSACT_DCERPCCMD); SSVAL(cli_nps->trans.setup+1, 0, cli_nps->fnum); *_stream = stream; tevent_req_received(req); return NT_STATUS_OK; } static ssize_t tstream_smbXcli_np_pending_bytes(struct tstream_context *stream) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); if (!smbXcli_conn_is_connected(cli_nps->conn)) { errno = ENOTCONN; return -1; } return cli_nps->read.left; } bool tstream_is_smbXcli_np(struct tstream_context *stream) { struct tstream_smbXcli_np *cli_nps = talloc_get_type(_tstream_context_data(stream), struct tstream_smbXcli_np); if (!cli_nps) { return false; } return true; } NTSTATUS tstream_smbXcli_np_use_trans(struct tstream_context *stream) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); if (cli_nps->trans.read_req) { return NT_STATUS_PIPE_BUSY; } if (cli_nps->trans.write_req) { return NT_STATUS_PIPE_BUSY; } if (cli_nps->trans.active) { return NT_STATUS_PIPE_BUSY; } cli_nps->trans.active = true; return NT_STATUS_OK; } unsigned int tstream_smbXcli_np_set_timeout(struct tstream_context *stream, unsigned int timeout) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); unsigned int old_timeout = cli_nps->timeout; cli_nps->timeout = timeout; return old_timeout; } struct tstream_smbXcli_np_writev_state { struct tstream_context *stream; struct tevent_context *ev; struct iovec *vector; size_t count; int ret; struct { int val; const char *location; } error; }; static int tstream_smbXcli_np_writev_state_destructor(struct tstream_smbXcli_np_writev_state *state) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np); cli_nps->trans.write_req = NULL; return 0; } static void tstream_smbXcli_np_writev_write_next(struct tevent_req *req); static struct tevent_req *tstream_smbXcli_np_writev_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream, const struct iovec *vector, size_t count) { struct tevent_req *req; struct tstream_smbXcli_np_writev_state *state; struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); req = tevent_req_create(mem_ctx, &state, struct tstream_smbXcli_np_writev_state); if (!req) { return NULL; } state->stream = stream; state->ev = ev; state->ret = 0; talloc_set_destructor(state, tstream_smbXcli_np_writev_state_destructor); if (!smbXcli_conn_is_connected(cli_nps->conn)) { tevent_req_error(req, ENOTCONN); return tevent_req_post(req, ev); } /* * we make a copy of the vector so we can change the structure */ state->vector = talloc_array(state, struct iovec, count); if (tevent_req_nomem(state->vector, req)) { return tevent_req_post(req, ev); } memcpy(state->vector, vector, sizeof(struct iovec) * count); state->count = count; tstream_smbXcli_np_writev_write_next(req); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); } return req; } static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *req); static void tstream_smbXcli_np_writev_write_done(struct tevent_req *subreq); static void tstream_smbXcli_np_writev_write_next(struct tevent_req *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); struct tevent_req *subreq; size_t i; size_t left = 0; for (i=0; i < state->count; i++) { left += state->vector[i].iov_len; } if (left == 0) { TALLOC_FREE(cli_nps->write.buf); tevent_req_done(req); return; } cli_nps->write.ofs = 0; cli_nps->write.left = MIN(left, TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); cli_nps->write.buf = talloc_realloc(cli_nps, cli_nps->write.buf, uint8_t, cli_nps->write.left); if (tevent_req_nomem(cli_nps->write.buf, req)) { return; } /* * copy the pending buffer first */ while (cli_nps->write.left > 0 && state->count > 0) { uint8_t *base = (uint8_t *)state->vector[0].iov_base; size_t len = MIN(cli_nps->write.left, state->vector[0].iov_len); memcpy(cli_nps->write.buf + cli_nps->write.ofs, base, len); base += len; state->vector[0].iov_base = base; state->vector[0].iov_len -= len; cli_nps->write.ofs += len; cli_nps->write.left -= len; if (state->vector[0].iov_len == 0) { state->vector += 1; state->count -= 1; } state->ret += len; } if (cli_nps->trans.active && state->count == 0) { cli_nps->trans.active = false; cli_nps->trans.write_req = req; return; } if (cli_nps->trans.read_req && state->count == 0) { cli_nps->trans.write_req = req; tstream_smbXcli_np_readv_trans_start(cli_nps->trans.read_req); return; } if (cli_nps->is_smb1) { subreq = smb1cli_writex_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->pid, cli_nps->tcon, cli_nps->session, cli_nps->fnum, 8, /* 8 means message mode. */ cli_nps->write.buf, 0, /* offset */ cli_nps->write.ofs); /* size */ } else { subreq = smb2cli_write_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, cli_nps->write.ofs, /* length */ 0, /* offset */ cli_nps->fid_persistent, cli_nps->fid_volatile, 0, /* remaining_bytes */ 0, /* flags */ cli_nps->write.buf); } if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, tstream_smbXcli_np_writev_write_done, req); } static void tstream_smbXcli_np_writev_disconnect_now(struct tevent_req *req, int error, const char *location); static void tstream_smbXcli_np_writev_write_done(struct tevent_req *subreq) { struct tevent_req *req = 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); uint32_t written; NTSTATUS status; if (cli_nps->is_smb1) { status = smb1cli_writex_recv(subreq, &written, NULL); } else { status = smb2cli_write_recv(subreq, &written); } TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { tstream_smbXcli_np_writev_disconnect_now(req, EPIPE, __location__); return; } if (written != cli_nps->write.ofs) { tstream_smbXcli_np_writev_disconnect_now(req, EIO, __location__); return; } tstream_smbXcli_np_writev_write_next(req); } static void tstream_smbXcli_np_writev_disconnect_done(struct tevent_req *subreq); static void tstream_smbXcli_np_writev_disconnect_now(struct tevent_req *req, int error, const char *location) { 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); struct tevent_req *subreq; state->error.val = error; state->error.location = location; if (!smbXcli_conn_is_connected(cli_nps->conn)) { /* return the original error */ _tevent_req_error(req, state->error.val, state->error.location); return; } 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); return; } tevent_req_set_callback(subreq, tstream_smbXcli_np_writev_disconnect_done, req); } static void tstream_smbXcli_np_writev_disconnect_done(struct tevent_req *subreq) { struct tevent_req *req = 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); int error; tstream_smbXcli_np_disconnect_recv(subreq, &error); TALLOC_FREE(subreq); /* return the original error */ _tevent_req_error(req, state->error.val, state->error.location); } static int tstream_smbXcli_np_writev_recv(struct tevent_req *req, int *perrno) { struct tstream_smbXcli_np_writev_state *state = tevent_req_data(req, struct tstream_smbXcli_np_writev_state); int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { ret = state->ret; } tevent_req_received(req); return ret; } struct tstream_smbXcli_np_readv_state { struct tstream_context *stream; struct tevent_context *ev; struct iovec *vector; size_t count; int ret; struct { struct tevent_immediate *im; } trans; struct { int val; const char *location; } error; }; static int tstream_smbXcli_np_readv_state_destructor(struct tstream_smbXcli_np_readv_state *state) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(state->stream, struct tstream_smbXcli_np); cli_nps->trans.read_req = NULL; return 0; } static void tstream_smbXcli_np_readv_read_next(struct tevent_req *req); static struct tevent_req *tstream_smbXcli_np_readv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct tstream_context *stream, struct iovec *vector, size_t count) { struct tevent_req *req; struct tstream_smbXcli_np_readv_state *state; struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); req = tevent_req_create(mem_ctx, &state, struct tstream_smbXcli_np_readv_state); if (!req) { return NULL; } state->stream = stream; state->ev = ev; state->ret = 0; talloc_set_destructor(state, tstream_smbXcli_np_readv_state_destructor); if (!smbXcli_conn_is_connected(cli_nps->conn)) { tevent_req_error(req, ENOTCONN); return tevent_req_post(req, ev); } /* * we make a copy of the vector so we can change the structure */ state->vector = talloc_array(state, struct iovec, count); if (tevent_req_nomem(state->vector, req)) { return tevent_req_post(req, ev); } memcpy(state->vector, vector, sizeof(struct iovec) * count); state->count = count; tstream_smbXcli_np_readv_read_next(req); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); } return req; } static void tstream_smbXcli_np_readv_read_done(struct tevent_req *subreq); static void tstream_smbXcli_np_readv_read_next(struct tevent_req *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); struct tevent_req *subreq; /* * copy the pending buffer first */ while (cli_nps->read.left > 0 && state->count > 0) { uint8_t *base = (uint8_t *)state->vector[0].iov_base; size_t len = MIN(cli_nps->read.left, state->vector[0].iov_len); memcpy(base, cli_nps->read.buf + cli_nps->read.ofs, len); base += len; state->vector[0].iov_base = base; state->vector[0].iov_len -= len; cli_nps->read.ofs += len; cli_nps->read.left -= len; if (state->vector[0].iov_len == 0) { state->vector += 1; state->count -= 1; } state->ret += len; } if (cli_nps->read.left == 0) { TALLOC_FREE(cli_nps->read.buf); } if (state->count == 0) { tevent_req_done(req); return; } if (cli_nps->trans.active) { cli_nps->trans.active = false; cli_nps->trans.read_req = req; return; } if (cli_nps->trans.write_req) { cli_nps->trans.read_req = req; tstream_smbXcli_np_readv_trans_start(req); return; } if (cli_nps->is_smb1) { subreq = smb1cli_readx_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->pid, cli_nps->tcon, cli_nps->session, cli_nps->fnum, 0, /* offset */ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); } else { subreq = smb2cli_read_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, /* length */ 0, /* offset */ cli_nps->fid_persistent, cli_nps->fid_volatile, 0, /* minimum_count */ 0); /* remaining_bytes */ } if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, tstream_smbXcli_np_readv_read_done, req); } static void tstream_smbXcli_np_readv_trans_done(struct tevent_req *subreq); static void tstream_smbXcli_np_readv_trans_start(struct tevent_req *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); struct tevent_req *subreq; state->trans.im = tevent_create_immediate(state); if (tevent_req_nomem(state->trans.im, req)) { return; } if (cli_nps->is_smb1) { subreq = smb1cli_trans_send(state, state->ev, cli_nps->conn, SMBtrans, 0, 0, /* *_flags */ 0, 0, /* *_flags2 */ cli_nps->timeout, cli_nps->pid, cli_nps->tcon, cli_nps->session, "\\PIPE\\", 0, 0, 0, cli_nps->trans.setup, 2, 0, NULL, 0, 0, cli_nps->write.buf, cli_nps->write.ofs, TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE); } else { DATA_BLOB in_input_buffer = data_blob_null; DATA_BLOB in_output_buffer = data_blob_null; in_input_buffer = data_blob_const(cli_nps->write.buf, cli_nps->write.ofs); subreq = smb2cli_ioctl_send(state, state->ev, cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, cli_nps->fid_persistent, cli_nps->fid_volatile, FSCTL_NAMED_PIPE_READ_WRITE, 0, /* in_max_input_length */ &in_input_buffer, /* in_max_output_length */ TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE, &in_output_buffer, SMB2_IOCTL_FLAG_IS_FSCTL); } if (tevent_req_nomem(subreq, req)) { return; } tevent_req_set_callback(subreq, tstream_smbXcli_np_readv_trans_done, req); } static void tstream_smbXcli_np_readv_disconnect_now(struct tevent_req *req, int error, const char *location); static void tstream_smbXcli_np_readv_trans_next(struct tevent_context *ctx, struct tevent_immediate *im, void *private_data); static void tstream_smbXcli_np_readv_trans_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); uint8_t *rcvbuf; uint32_t received; NTSTATUS status; if (cli_nps->is_smb1) { status = smb1cli_trans_recv(subreq, state, NULL, NULL, 0, NULL, NULL, 0, NULL, &rcvbuf, 0, &received); } else { DATA_BLOB out_input_buffer = data_blob_null; DATA_BLOB out_output_buffer = data_blob_null; status = smb2cli_ioctl_recv(subreq, state, &out_input_buffer, &out_output_buffer); /* Note that rcvbuf is not a talloc pointer here */ rcvbuf = out_output_buffer.data; received = out_output_buffer.length; } TALLOC_FREE(subreq); 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, EPIPE, __location__); return; } if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) { tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__); return; } if (received == 0) { tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__); return; } cli_nps->read.ofs = 0; cli_nps->read.left = received; 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); return; } memcpy(cli_nps->read.buf, rcvbuf, received); if (cli_nps->trans.write_req == NULL) { tstream_smbXcli_np_readv_read_next(req); return; } tevent_schedule_immediate(state->trans.im, state->ev, tstream_smbXcli_np_readv_trans_next, req); tevent_req_done(cli_nps->trans.write_req); } static void tstream_smbXcli_np_readv_trans_next(struct tevent_context *ctx, struct tevent_immediate *im, void *private_data) { struct tevent_req *req = talloc_get_type_abort(private_data, struct tevent_req); tstream_smbXcli_np_readv_read_next(req); } static void tstream_smbXcli_np_readv_read_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); uint8_t *rcvbuf; uint32_t received; NTSTATUS status; /* * We must free subreq in this function as there is * a timer event attached to it. */ if (cli_nps->is_smb1) { status = smb1cli_readx_recv(subreq, &received, &rcvbuf); } else { status = smb2cli_read_recv(subreq, state, &rcvbuf, &received); } /* * We can't TALLOC_FREE(subreq) as usual here, as rcvbuf still is a * child of that. */ 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)) { TALLOC_FREE(subreq); tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__); return; } if (received > TSTREAM_SMBXCLI_NP_MAX_BUF_SIZE) { TALLOC_FREE(subreq); tstream_smbXcli_np_readv_disconnect_now(req, EIO, __location__); return; } if (received == 0) { TALLOC_FREE(subreq); tstream_smbXcli_np_readv_disconnect_now(req, EPIPE, __location__); return; } cli_nps->read.ofs = 0; cli_nps->read.left = received; 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); return; } memcpy(cli_nps->read.buf, rcvbuf, received); TALLOC_FREE(subreq); tstream_smbXcli_np_readv_read_next(req); } static void tstream_smbXcli_np_readv_disconnect_done(struct tevent_req *subreq); static void tstream_smbXcli_np_readv_error(struct tevent_req *req); static void tstream_smbXcli_np_readv_disconnect_now(struct tevent_req *req, int error, const char *location) { 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); struct tevent_req *subreq; state->error.val = error; state->error.location = location; if (!smbXcli_conn_is_connected(cli_nps->conn)) { /* return the original error */ tstream_smbXcli_np_readv_error(req); return; } subreq = tstream_smbXcli_np_disconnect_send(state, state->ev, state->stream); if (subreq == NULL) { /* return the original error */ tstream_smbXcli_np_readv_error(req); return; } tevent_req_set_callback(subreq, tstream_smbXcli_np_readv_disconnect_done, req); } static void tstream_smbXcli_np_readv_disconnect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); int error; tstream_smbXcli_np_disconnect_recv(subreq, &error); TALLOC_FREE(subreq); tstream_smbXcli_np_readv_error(req); } static void tstream_smbXcli_np_readv_error_trigger(struct tevent_context *ctx, struct tevent_immediate *im, void *private_data); static void tstream_smbXcli_np_readv_error(struct tevent_req *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); if (cli_nps->trans.write_req == NULL) { /* return the original error */ _tevent_req_error(req, state->error.val, state->error.location); return; } if (state->trans.im == NULL) { /* return the original error */ _tevent_req_error(req, state->error.val, state->error.location); return; } tevent_schedule_immediate(state->trans.im, state->ev, tstream_smbXcli_np_readv_error_trigger, req); /* return the original error for writev */ _tevent_req_error(cli_nps->trans.write_req, state->error.val, state->error.location); } static void tstream_smbXcli_np_readv_error_trigger(struct tevent_context *ctx, struct tevent_immediate *im, void *private_data) { struct tevent_req *req = talloc_get_type_abort(private_data, struct tevent_req); struct tstream_smbXcli_np_readv_state *state = tevent_req_data(req, struct tstream_smbXcli_np_readv_state); /* return the original error */ _tevent_req_error(req, state->error.val, state->error.location); } static int tstream_smbXcli_np_readv_recv(struct tevent_req *req, int *perrno) { struct tstream_smbXcli_np_readv_state *state = tevent_req_data(req, struct tstream_smbXcli_np_readv_state); int ret; ret = tsocket_simple_int_recv(req, perrno); if (ret == 0) { ret = state->ret; } tevent_req_received(req); return ret; } 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, struct tstream_context *stream) { struct tstream_smbXcli_np *cli_nps = tstream_context_data(stream, struct tstream_smbXcli_np); struct tevent_req *req; struct tstream_smbXcli_np_disconnect_state *state; struct tevent_req *subreq; req = tevent_req_create(mem_ctx, &state, struct tstream_smbXcli_np_disconnect_state); if (req == NULL) { return NULL; } state->stream = stream; if (!smbXcli_conn_is_connected(cli_nps->conn)) { tevent_req_error(req, ENOTCONN); return tevent_req_post(req, ev); } if (cli_nps->is_smb1) { subreq = smb1cli_close_send(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, ev, cli_nps->conn, cli_nps->timeout, cli_nps->session, cli_nps->tcon, 0, /* flags */ cli_nps->fid_persistent, cli_nps->fid_volatile); } if (tevent_req_nomem(subreq, req)) { 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; } static void tstream_smbXcli_np_disconnect_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct tstream_smbXcli_np_disconnect_state *state = tevent_req_data(req, struct tstream_smbXcli_np_disconnect_state); struct tstream_smbXcli_np *cli_nps = 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 { status = smb2cli_close_recv(subreq); } TALLOC_FREE(subreq); if (!NT_STATUS_IS_OK(status)) { tevent_req_error(req, EPIPE); return; } cli_nps->conn = NULL; cli_nps->session = NULL; cli_nps->tcon = NULL; 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) { int ret; ret = tsocket_simple_int_recv(req, perrno); tevent_req_received(req); return ret; } static const struct tstream_context_ops tstream_smbXcli_np_ops = { .name = "smbXcli_np", .pending_bytes = tstream_smbXcli_np_pending_bytes, .readv_send = tstream_smbXcli_np_readv_send, .readv_recv = tstream_smbXcli_np_readv_recv, .writev_send = tstream_smbXcli_np_writev_send, .writev_recv = tstream_smbXcli_np_writev_recv, .disconnect_send = tstream_smbXcli_np_disconnect_send, .disconnect_recv = tstream_smbXcli_np_disconnect_recv, };