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 2 of the License, or
+ 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,
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, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
this implements the IPC$ backend, called by the NTVFS subsystem to
#include "includes.h"
-#include "dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "ntvfs/ntvfs.h"
#include "libcli/rap/rap.h"
#include "ntvfs/ipc/proto.h"
-#include "rpc_server/dcerpc_server.h"
+#include "libcli/raw/ioctl.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "lib/socket/socket.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include <gssapi/gssapi.h>
+#include "system/locale.h"
/* this is the private structure used to keep the state of an open
ipc$ connection. It needs to keep information about all open
struct ipc_private {
struct ntvfs_module_context *ntvfs;
- struct dcesrv_context *dcesrv;
-
/* a list of open pipes */
struct pipe_state {
struct pipe_state *next, *prev;
- struct ipc_private *private;
+ struct ipc_private *ipriv;
const char *pipe_name;
struct ntvfs_handle *handle;
- struct dcesrv_connection *dce_conn;
- uint16_t ipc_state;
+ struct tstream_context *npipe;
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t allocation_size;
+ struct tevent_queue *write_queue;
+ struct tevent_queue *read_queue;
} *pipe_list;
};
/*
find a open pipe give a file handle
*/
-static struct pipe_state *pipe_state_find(struct ipc_private *private, struct ntvfs_handle *handle)
+static struct pipe_state *pipe_state_find(struct ipc_private *ipriv, struct ntvfs_handle *handle)
{
struct pipe_state *s;
void *p;
- p = ntvfs_handle_get_backend_data(handle, private->ntvfs);
+ p = ntvfs_handle_get_backend_data(handle, ipriv->ntvfs);
if (!p) return NULL;
s = talloc_get_type(p, struct pipe_state);
/*
find a open pipe give a wire fnum
*/
-static struct pipe_state *pipe_state_find_key(struct ipc_private *private, struct ntvfs_request *req, const DATA_BLOB *key)
+static struct pipe_state *pipe_state_find_key(struct ipc_private *ipriv, struct ntvfs_request *req, const DATA_BLOB *key)
{
struct ntvfs_handle *h;
- h = ntvfs_handle_search_by_wire_key(private->ntvfs, req, key);
+ h = ntvfs_handle_search_by_wire_key(ipriv->ntvfs, req, key);
if (!h) return NULL;
- return pipe_state_find(private, h);
+ return pipe_state_find(ipriv, h);
}
connect to a share - always works
*/
static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, const char *sharename)
+ struct ntvfs_request *req,
+ union smb_tcon* tcon)
{
- NTSTATUS status;
- struct ipc_private *private;
+ struct ipc_private *ipriv;
+ const char *sharename;
+
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *p = strchr(sharename+2, '\\');
+ if (p) {
+ sharename = p + 1;
+ }
+ }
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "IPC");
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
- /* prepare the private state for this connection */
- private = talloc(ntvfs, struct ipc_private);
- NT_STATUS_HAVE_NO_MEMORY(private);
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
+ }
- ntvfs->private_data = private;
+ /* prepare the private state for this connection */
+ ipriv = talloc(ntvfs, struct ipc_private);
+ NT_STATUS_HAVE_NO_MEMORY(ipriv);
- private->ntvfs = ntvfs;
- private->pipe_list = NULL;
+ ntvfs->private_data = ipriv;
- /* setup the DCERPC server subsystem */
- status = dcesrv_init_ipc_context(private, &private->dcesrv);
- NT_STATUS_NOT_OK_RETURN(status);
+ ipriv->ntvfs = ntvfs;
+ ipriv->pipe_list = NULL;
return NT_STATUS_OK;
}
return NT_STATUS_ACCESS_DENIED;
}
-
-/*
- ioctl interface - we don't do any
-*/
-static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_ioctl *io)
-{
- return NT_STATUS_ACCESS_DENIED;
-}
-
/*
check if a directory exists
*/
static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fileinfo *info)
{
- return NT_STATUS_ACCESS_DENIED;
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ case RAW_FILEINFO_GETATTR:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
}
/*
*/
static int ipc_fd_destructor(struct pipe_state *p)
{
- DLIST_REMOVE(p->private->pipe_list, p);
+ DLIST_REMOVE(p->ipriv->pipe_list, p);
+ ntvfs_handle_remove_backend_data(p->handle, p->ipriv->ntvfs);
return 0;
}
-static struct socket_address *ipc_get_my_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
-{
- struct ipc_private *private = dce_conn->transport.private_data;
+struct ipc_open_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_open *oi;
+ struct netr_SamInfo3 *info3;
+};
- return ntvfs_get_my_addr(private->ntvfs, mem_ctx);
-}
+static void ipc_open_done(struct tevent_req *subreq);
-static struct socket_address *ipc_get_peer_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
+/*
+ check the pipename is valid
+ */
+static NTSTATUS validate_pipename(const char *name)
{
- struct ipc_private *private = dce_conn->transport.private_data;
-
- return ntvfs_get_peer_addr(private->ntvfs, mem_ctx);
+ while (*name) {
+ if (!isalnum(*name)) return NT_STATUS_INVALID_PARAMETER;
+ name++;
+ }
+ return NT_STATUS_OK;
}
/*
- open a file backend - used for MSRPC pipes
+ open a file - used for MSRPC pipes
*/
-static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, const char *fname,
- struct pipe_state **ps)
+static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
{
- struct pipe_state *p;
NTSTATUS status;
- struct dcerpc_binding *ep_description;
- struct ipc_private *private = ntvfs->private_data;
+ struct pipe_state *p;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct smb_iconv_convenience *smb_ic
+ = lp_iconv_convenience(ipriv->ntvfs->ctx->lp_ctx);
struct ntvfs_handle *h;
+ struct ipc_open_state *state;
+ struct tevent_req *subreq;
+ const char *fname;
+ const char *directory;
+ struct socket_address *client_sa;
+ struct tsocket_address *client_addr;
+ struct socket_address *server_sa;
+ struct tsocket_address *server_addr;
+ int ret;
+ DATA_BLOB delegated_creds = data_blob_null;
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ case RAW_OPEN_NTTRANS_CREATE:
+ fname = oi->ntcreatex.in.fname;
+ break;
+ case RAW_OPEN_OPENX:
+ fname = oi->openx.in.fname;
+ break;
+ case RAW_OPEN_SMB2:
+ fname = oi->smb2.in.fname;
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ directory = talloc_asprintf(req, "%s/np",
+ lp_ncalrpc_dir(ipriv->ntvfs->ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(directory);
+
+ state = talloc(req, struct ipc_open_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
status = ntvfs_handle_new(ntvfs, req, &h);
NT_STATUS_NOT_OK_RETURN(status);
p = talloc(h, struct pipe_state);
NT_STATUS_HAVE_NO_MEMORY(p);
- ep_description = talloc(req, struct dcerpc_binding);
- NT_STATUS_HAVE_NO_MEMORY(ep_description);
-
while (fname[0] == '\\') fname++;
+ /* check for valid characters in name */
+ fname = strlower_talloc(p, fname);
+
+ status = validate_pipename(fname);
+ NT_STATUS_NOT_OK_RETURN(status);
+
p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
p->handle = h;
- p->ipc_state = 0x5ff;
-
- /*
- we're all set, now ask the dcerpc server subsystem to open the
- endpoint. At this stage the pipe isn't bound, so we don't
- know what interface the user actually wants, just that they want
- one of the interfaces attached to this pipe endpoint.
- */
- ep_description->transport = NCACN_NP;
- ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
-
- /* The session info is refcount-increased in the
- * dcesrv_endpoint_search_connect() function
- */
- status = dcesrv_endpoint_search_connect(private->dcesrv,
- p,
- ep_description,
- h->session_info,
- ntvfs->ctx->event_ctx,
- ntvfs->ctx->msg_ctx,
- ntvfs->ctx->server_id,
- 0,
- &p->dce_conn);
- NT_STATUS_NOT_OK_RETURN(status);
+ p->ipriv = ipriv;
- p->dce_conn->transport.private_data = private;
- p->dce_conn->transport.report_output_data = NULL;
- p->dce_conn->transport.get_my_addr = ipc_get_my_addr;
- p->dce_conn->transport.get_peer_addr = ipc_get_peer_addr;
-
- DLIST_ADD(private->pipe_list, p);
+ p->write_queue = tevent_queue_create(p, "ipc_write_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->write_queue);
- p->private = private;
+ p->read_queue = tevent_queue_create(p, "ipc_read_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->read_queue);
- talloc_set_destructor(p, ipc_fd_destructor);
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->oi = oi;
- status = ntvfs_handle_set_backend_data(h, private->ntvfs, p);
+ status = auth_convert_server_info_saminfo3(state,
+ req->session_info->server_info,
+ &state->info3);
NT_STATUS_NOT_OK_RETURN(status);
- *ps = p;
- return NT_STATUS_OK;
-}
+ client_sa = ntvfs_get_peer_addr(ntvfs, state);
+ if (!client_sa) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
-/*
- open a file with ntcreatex - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
-{
- struct pipe_state *p;
- NTSTATUS status;
+ server_sa = ntvfs_get_my_addr(ntvfs, state);
+ if (!server_sa) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
- if (!NT_STATUS_IS_OK(status)) {
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ client_sa->addr,
+ client_sa->port,
+ &client_addr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
return status;
}
- ZERO_STRUCT(oi->ntcreatex.out);
- oi->ntcreatex.out.file.ntvfs= p->handle;
- oi->ntcreatex.out.ipc_state = p->ipc_state;
- oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ server_sa->addr,
+ server_sa->port,
+ &server_addr);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(errno);
+ return status;
+ }
- return status;
-}
+ if (req->session_info->credentials) {
+ struct gssapi_creds_container *gcc;
+ OM_uint32 gret;
+ OM_uint32 minor_status;
+ gss_buffer_desc cred_token;
+
+ ret = cli_credentials_get_client_gss_creds(req->session_info->credentials,
+ ipriv->ntvfs->ctx->event_ctx,
+ ipriv->ntvfs->ctx->lp_ctx,
+ &gcc);
+ if (ret) {
+ goto skip;
+ }
-/*
- open a file with openx - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
-{
- struct pipe_state *p;
- NTSTATUS status;
- const char *fname = oi->openx.in.fname;
+ gret = gss_export_cred(&minor_status,
+ gcc->creds,
+ &cred_token);
+ if (gret != GSS_S_COMPLETE) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- status = ipc_open_generic(ntvfs, req, fname, &p);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ if (cred_token.length) {
+ delegated_creds = data_blob_talloc(req,
+ cred_token.value,
+ cred_token.length);
+ gss_release_buffer(&minor_status, &cred_token);
+ NT_STATUS_HAVE_NO_MEMORY(delegated_creds.data);
+ }
}
- ZERO_STRUCT(oi->openx.out);
- oi->openx.out.file.ntvfs= p->handle;
- oi->openx.out.ftype = 2;
- oi->openx.out.devstate = p->ipc_state;
-
- return status;
+skip:
+
+ subreq = tstream_npa_connect_send(p,
+ ipriv->ntvfs->ctx->event_ctx,
+ smb_ic,
+ directory,
+ fname,
+ client_addr,
+ NULL,
+ server_addr,
+ NULL,
+ state->info3,
+ req->session_info->session_key,
+ delegated_creds);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_open_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
}
-/*
- open a file - used for MSRPC pipes
-*/
-static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
- struct ntvfs_request *req, union smb_open *oi)
+static void ipc_open_done(struct tevent_req *subreq)
{
+ struct ipc_open_state *state = tevent_req_callback_data(subreq,
+ struct ipc_open_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ union smb_open *oi = state->oi;
+ int ret;
+ int sys_errno;
NTSTATUS status;
+ ret = tstream_npa_connect_recv(subreq, &sys_errno,
+ p, &p->npipe,
+ &p->file_type,
+ &p->device_state,
+ &p->allocation_size);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ DLIST_ADD(ipriv->pipe_list, p);
+ talloc_set_destructor(p, ipc_fd_destructor);
+
+ status = ntvfs_handle_set_backend_data(p->handle, ipriv->ntvfs, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto reply;
+ }
+
switch (oi->generic.level) {
case RAW_OPEN_NTCREATEX:
- status = ipc_open_ntcreatex(ntvfs, req, oi);
+ ZERO_STRUCT(oi->ntcreatex.out);
+ oi->ntcreatex.out.file.ntvfs = p->handle;
+ oi->ntcreatex.out.oplock_level = 0;
+ oi->ntcreatex.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->ntcreatex.out.create_time = 0;
+ oi->ntcreatex.out.access_time = 0;
+ oi->ntcreatex.out.write_time = 0;
+ oi->ntcreatex.out.change_time = 0;
+ oi->ntcreatex.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->ntcreatex.out.alloc_size = p->allocation_size;
+ oi->ntcreatex.out.size = 0;
+ oi->ntcreatex.out.file_type = p->file_type;
+ oi->ntcreatex.out.ipc_state = p->device_state;
+ oi->ntcreatex.out.is_directory = 0;
break;
case RAW_OPEN_OPENX:
- status = ipc_open_openx(ntvfs, req, oi);
+ ZERO_STRUCT(oi->openx.out);
+ oi->openx.out.file.ntvfs = p->handle;
+ oi->openx.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->openx.out.write_time = 0;
+ oi->openx.out.size = 0;
+ oi->openx.out.access = 0;
+ oi->openx.out.ftype = p->file_type;
+ oi->openx.out.devstate = p->device_state;
+ oi->openx.out.action = 0;
+ oi->openx.out.unique_fid = 0;
+ oi->openx.out.access_mask = 0;
+ oi->openx.out.unknown = 0;
+ break;
+ case RAW_OPEN_SMB2:
+ ZERO_STRUCT(oi->smb2.out);
+ oi->smb2.out.file.ntvfs = p->handle;
+ oi->smb2.out.oplock_level = oi->smb2.in.oplock_level;
+ oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->smb2.out.create_time = 0;
+ oi->smb2.out.access_time = 0;
+ oi->smb2.out.write_time = 0;
+ oi->smb2.out.change_time = 0;
+ oi->smb2.out.alloc_size = p->allocation_size;
+ oi->smb2.out.size = 0;
+ oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
+ oi->smb2.out.reserved2 = 0;
break;
default:
- status = NT_STATUS_NOT_SUPPORTED;
break;
}
- return status;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
/*
return NT_STATUS_ACCESS_DENIED;
}
-static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
+struct ipc_readv_next_vector_state {
+ uint8_t *buf;
+ size_t len;
+ off_t ofs;
+ size_t remaining;
+};
+
+static void ipc_readv_next_vector_init(struct ipc_readv_next_vector_state *s,
+ uint8_t *buf, size_t len)
+{
+ ZERO_STRUCTP(s);
+
+ s->buf = buf;
+ s->len = MIN(len, UINT16_MAX);
+ //DEBUG(0,("readv_next_vector_init[%u 0x%04X]\n", s->len, s->len));
+}
+
+static int ipc_readv_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
{
- DATA_BLOB *blob = private_data;
+ struct ipc_readv_next_vector_state *state =
+ (struct ipc_readv_next_vector_state *)private_data;
+ struct iovec *vector;
+ ssize_t pending;
+ size_t wanted;
+
+ if (state->ofs == state->len) {
+ *_vector = NULL;
+ *count = 0;
+// DEBUG(0,("readv_next_vector done ofs[%u 0x%04X]\n",
+// state->ofs, state->ofs));
+ return 0;
+ }
- if (out->length < blob->length) {
- blob->length = out->length;
+ pending = tstream_pending_bytes(stream);
+ if (pending == -1) {
+ return -1;
}
- memcpy(blob->data, out->data, blob->length);
- *nwritten = blob->length;
- return NT_STATUS_OK;
+
+ if (pending == 0 && state->ofs != 0) {
+ /* return a short read */
+ *_vector = NULL;
+ *count = 0;
+// DEBUG(0,("readv_next_vector short read ofs[%u 0x%04X]\n",
+// state->ofs, state->ofs));
+ return 0;
+ }
+
+ if (pending == 0) {
+ /* we want at least one byte and recheck again */
+ wanted = 1;
+ } else {
+ size_t missing = state->len - state->ofs;
+ if (pending > missing) {
+ /* there's more available */
+ state->remaining = pending - missing;
+ wanted = missing;
+ } else {
+ /* read what we can get and recheck in the next cycle */
+ wanted = pending;
+ }
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = state->buf + state->ofs;
+ vector[0].iov_len = wanted;
+
+ state->ofs += wanted;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
}
+struct ipc_read_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_read *rd;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_read_done(struct tevent_req *subreq);
+
/*
read from a file
*/
static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_read *rd)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- NTSTATUS status = NT_STATUS_OK;
+ struct ipc_read_state *state;
+ struct tevent_req *subreq;
if (rd->generic.level != RAW_READ_GENERIC) {
return ntvfs_map_read(ntvfs, req, rd);
}
- p = pipe_state_find(private, rd->readx.in.file.ntvfs);
+ p = pipe_state_find(ipriv, rd->readx.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- data.length = rd->readx.in.maxcnt;
- data.data = rd->readx.out.data;
- if (data.length > UINT16_MAX) {
- data.length = UINT16_MAX;
+ state = talloc(req, struct ipc_read_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->rd = rd;
+
+ /* rd->readx.out.data is already allocated */
+ ipc_readv_next_vector_init(&state->next_vector,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt);
+
+ subreq = tstream_readv_pdu_queue_send(req,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_read_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_read_done(struct tevent_req *subreq)
+{
+ struct ipc_read_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_read_state);
+ struct ntvfs_request *req = state->req;
+ union smb_read *rd = state->rd;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
}
- if (data.length != 0) {
- status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
- if (NT_STATUS_IS_ERR(status)) {
- return status;
- }
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
}
- rd->readx.out.remaining = 0;
+ rd->readx.out.remaining = state->next_vector.remaining;
rd->readx.out.compaction_mode = 0;
- rd->readx.out.nread = data.length;
+ rd->readx.out.nread = ret;
- return status;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
+struct ipc_write_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_write *wr;
+ struct iovec iov;
+};
+
+static void ipc_write_done(struct tevent_req *subreq);
+
/*
write to a file
*/
static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_write *wr)
{
- struct ipc_private *private = ntvfs->private_data;
- DATA_BLOB data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- NTSTATUS status;
+ struct tevent_req *subreq;
+ struct ipc_write_state *state;
if (wr->generic.level != RAW_WRITE_GENERIC) {
return ntvfs_map_write(ntvfs, req, wr);
}
- data.data = discard_const_p(void, wr->writex.in.data);
- data.length = wr->writex.in.count;
-
- p = pipe_state_find(private, wr->writex.in.file.ntvfs);
+ p = pipe_state_find(ipriv, wr->writex.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- status = dcesrv_input(p->dce_conn, &data);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ state = talloc(req, struct ipc_write_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->wr = wr;
+ state->iov.iov_base = discard_const_p(void, wr->writex.in.data);
+ state->iov.iov_len = wr->writex.in.count;
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_write_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_write_done(struct tevent_req *subreq)
+{
+ struct ipc_write_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_write_state);
+ struct ntvfs_request *req = state->req;
+ union smb_write *wr = state->wr;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
}
- wr->writex.out.nwritten = data.length;
+ status = NT_STATUS_OK;
+
+ wr->writex.out.nwritten = ret;
wr->writex.out.remaining = 0;
- return NT_STATUS_OK;
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
/*
static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_close *io)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
if (io->generic.level != RAW_CLOSE_CLOSE) {
return ntvfs_map_close(ntvfs, req, io);
}
- p = pipe_state_find(private, io->close.in.file.ntvfs);
+ p = pipe_state_find(ipriv, io->close.in.file.ntvfs);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p, *next;
- for (p=private->pipe_list; p; p=next) {
+ for (p=ipriv->pipe_list; p; p=next) {
next = p->next;
if (p->handle->session_info == req->session_info &&
p->handle->smbpid == req->smbpid) {
static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p, *next;
- for (p=private->pipe_list; p; p=next) {
+ for (p=ipriv->pipe_list; p; p=next) {
next = p->next;
if (p->handle->session_info == req->session_info) {
talloc_free(p);
*/
static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
- void *private)
+ void *private_data)
{
return NT_STATUS_OK;
}
static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_fileinfo *info)
{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p = pipe_state_find(ipriv, info->generic.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ {
+ ZERO_STRUCT(info->generic.out);
+ info->generic.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ info->generic.out.fname.s = strrchr(p->pipe_name, '\\');
+ info->generic.out.alloc_size = 4096;
+ info->generic.out.nlink = 1;
+ /* What the heck? Match Win2k3: IPC$ pipes are delete pending */
+ info->generic.out.delete_pending = 1;
+ return NT_STATUS_OK;
+ }
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return NT_STATUS_INVALID_PARAMETER;
+ case RAW_FILEINFO_ALL_EAS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
return NT_STATUS_ACCESS_DENIED;
}
static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_first *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_search_next *io,
void *search_private,
- BOOL (*callback)(void *, union smb_search_data *))
+ bool (*callback)(void *, const union smb_search_data *))
{
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_ACCESS_DENIED;
}
-static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
-{
- NTSTATUS status = NT_STATUS_OK;
- DATA_BLOB *blob = private_data;
-
- if (out->length > blob->length) {
- status = STATUS_BUFFER_OVERFLOW;
- }
+struct ipc_trans_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ struct smb_trans2 *trans;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
- if (out->length < blob->length) {
- blob->length = out->length;
- }
- memcpy(blob->data, out->data, blob->length);
- *nwritten = blob->length;
- return status;
-}
+static void ipc_trans_writev_done(struct tevent_req *subreq);
+static void ipc_trans_readv_done(struct tevent_req *subreq);
/* SMBtrans - handle a DCERPC command */
static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_trans2 *trans)
{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
- struct ipc_private *private = ntvfs->private_data;
- NTSTATUS status;
DATA_BLOB fnum_key;
uint16_t fnum;
+ struct ipc_trans_state *state;
+ struct tevent_req *subreq;
/*
* the fnum is in setup[1], a 16 bit value
SSVAL(&fnum, 0, trans->in.setup[1]);
fnum_key = data_blob_const(&fnum, 2);
- p = pipe_state_find_key(private, req, &fnum_key);
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
- trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
- if (!trans->out.data.data) {
- return NT_STATUS_NO_MEMORY;
- }
-
- /* pass the data to the dcerpc server. Note that we don't
- expect this to fail, and things like NDR faults are not
- reported at this stage. Those sorts of errors happen in the
- dcesrv_output stage */
- status = dcesrv_input(p->dce_conn, &trans->in.data);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
/*
- now ask the dcerpc system for some output. This doesn't yet handle
- async calls. Again, we only expect NT_STATUS_OK. If the call fails then
- the error is encoded at the dcerpc level
- */
- status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
- if (NT_STATUS_IS_ERR(status)) {
- return status;
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
}
+ state = talloc(req, struct ipc_trans_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
trans->out.setup_count = 0;
trans->out.setup = NULL;
trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->trans = trans;
+ state->writev_iov.iov_base = trans->in.data.data;
+ state->writev_iov.iov_len = trans->in.data.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ trans->out.data.data,
+ trans->out.data.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_trans_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
- return status;
+static void ipc_trans_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == 0) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ goto reply;
+ } else if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_trans_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
}
+static void ipc_trans_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ntvfs_request *req = state->req;
+ struct smb_trans2 *trans = state->trans;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ trans->out.data.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
/* SMBtrans - set named pipe state */
static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, struct smb_trans2 *trans)
{
- struct ipc_private *private = ntvfs->private_data;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
struct pipe_state *p;
DATA_BLOB fnum_key;
/* the fnum is in setup[1] */
fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
- p = pipe_state_find_key(private, req, &fnum_key);
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
if (!p) {
return NT_STATUS_INVALID_HANDLE;
}
if (trans->in.params.length != 2) {
return NT_STATUS_INVALID_PARAMETER;
}
- p->ipc_state = SVAL(trans->in.params.data, 0);
+
+ /*
+ * TODO: pass this to the tstream_npa logic
+ */
+ p->device_state = SVAL(trans->in.params.data, 0);
trans->out.setup_count = 0;
trans->out.setup = NULL;
NTSTATUS status;
if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
- return ipc_rap_call(req, trans);
+ return ipc_rap_call(req, ntvfs->ctx->event_ctx, ntvfs->ctx->lp_ctx, trans);
if (trans->in.setup_count != 2) {
return NT_STATUS_INVALID_PARAMETER;
return status;
}
+struct ipc_ioctl_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_ioctl *io;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq);
+static void ipc_ioctl_readv_done(struct tevent_req *subreq);
+
+static NTSTATUS ipc_ioctl_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ struct ipc_ioctl_state *state;
+ struct tevent_req *subreq;
+
+ switch (io->smb2.in.function) {
+ case FSCTL_NAMED_PIPE_READ_WRITE:
+ break;
+
+ default:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+ }
+
+ p = pipe_state_find(ipriv, io->smb2.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /*
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ state = talloc(req, struct ipc_ioctl_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ io->smb2.out._pad = 0;
+ io->smb2.out.function = io->smb2.in.function;
+ io->smb2.out.unknown2 = 0;
+ io->smb2.out.unknown3 = 0;
+ io->smb2.out.in = io->smb2.in.out;
+ io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_response_size);
+ NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->io = io;
+ state->writev_iov.iov_base = io->smb2.in.out.data;
+ state->writev_iov.iov_len = io->smb2.in.out.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ io->smb2.out.out.data,
+ io->smb2.out.out.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_ioctl_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_ioctl_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+static void ipc_ioctl_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ntvfs_request *req = state->req;
+ union smb_ioctl *io = state->io;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ io->smb2.out.out.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_SMB2:
+ return ipc_ioctl_smb2(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ return NT_STATUS_ACCESS_DENIED;
+}
/*