/*
Unix SMB/CIFS implementation.
default IPC$ NTVFS backend
+
Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
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
#include "includes.h"
+/* this is the private structure used to keep the state of an open
+ ipc$ connection. It needs to keep information about all open
+ pipes */
+struct ipc_private {
+
+ uint16_t next_fnum;
+ uint16_t num_open;
+
+ /* a list of open pipes */
+ struct pipe_state {
+ struct pipe_state *next, *prev;
+ TALLOC_CTX *mem_ctx;
+ const char *pipe_name;
+ uint16_t fnum;
+ struct dcesrv_connection *dce_conn;
+ uint16_t ipc_state;
+ } *pipe_list;
+
+};
+
+
+/*
+ find the next fnum available on this connection
+*/
+static uint16_t find_next_fnum(struct ipc_private *ipc)
+{
+ struct pipe_state *p;
+ uint32_t ret;
+
+ if (ipc->num_open == 0xFFFF) {
+ return 0;
+ }
+
+again:
+ ret = ipc->next_fnum++;
+
+ for (p=ipc->pipe_list; p; p=p->next) {
+ if (p->fnum == ret) {
+ goto again;
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ shutdown a single pipe. Called on a close or disconnect
+*/
+static void pipe_shutdown(struct ipc_private *private, struct pipe_state *p)
+{
+ TALLOC_CTX *mem_ctx = private->pipe_list->mem_ctx;
+ dcesrv_endpoint_disconnect(private->pipe_list->dce_conn);
+ DLIST_REMOVE(private->pipe_list, private->pipe_list);
+ talloc_destroy(mem_ctx);
+}
+
+
+/*
+ find a open pipe give a file descriptor
+*/
+static struct pipe_state *pipe_state_find(struct ipc_private *private, uint16_t fnum)
+{
+ struct pipe_state *p;
+
+ for (p=private->pipe_list; p; p=p->next) {
+ if (p->fnum == fnum) {
+ return p;
+ }
+ }
+
+ return NULL;
+}
+
+
/*
connect to a share - always works
*/
static NTSTATUS ipc_connect(struct request_context *req, const char *sharename)
{
struct tcon_context *conn = req->conn;
+ struct ipc_private *private;
conn->fs_type = talloc_strdup(conn->mem_ctx, "IPC");
conn->dev_type = talloc_strdup(conn->mem_ctx, "IPC");
+ /* prepare the private state for this connection */
+ private = talloc(conn->mem_ctx, sizeof(struct ipc_private));
+ if (!private) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn->ntvfs_private = (void *)private;
+
+ private->pipe_list = NULL;
+ private->next_fnum = 1;
+ private->num_open = 0;
+
return NT_STATUS_OK;
}
*/
static NTSTATUS ipc_disconnect(struct tcon_context *tcon)
{
+ struct ipc_private *private = tcon->ntvfs_private;
+
+ /* close any pipes that are open. Discard any unread data */
+ while (private->pipe_list) {
+ pipe_shutdown(private, private->pipe_list);
+ }
+
return NT_STATUS_OK;
}
/*
ioctl interface - we don't do any
*/
-static NTSTATUS ipc_ioctl(struct request_context *req, struct smb_ioctl *io)
+static NTSTATUS ipc_ioctl(struct request_context *req, union smb_ioctl *io)
{
return NT_STATUS_ACCESS_DENIED;
}
return NT_STATUS_ACCESS_DENIED;
}
+
+
+/*
+ open a file backend - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_generic(struct request_context *req, const char *fname,
+ struct pipe_state **ps)
+{
+ struct pipe_state *p;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct dcesrv_ep_description ep_description;
+ struct auth_session_info *session_info = NULL;
+ struct ipc_private *private = req->conn->ntvfs_private;
+
+ mem_ctx = talloc_init("ipc_open '%s'", fname);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = talloc(mem_ctx, sizeof(struct pipe_state));
+ if (!p) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ p->mem_ctx = mem_ctx;
+
+ p->pipe_name = talloc_strdup(mem_ctx, fname);
+ if (!p->pipe_name) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p->fnum = find_next_fnum(private);
+ if (p->fnum == 0) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_TOO_MANY_OPENED_FILES;
+ }
+
+ while (p->pipe_name[0] == '\\') {
+ p->pipe_name++;
+ }
+ 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.
+
+ TODO: note that we aren't passing any credentials here. We
+ will need to do that once the credentials infrastructure is
+ finalised for Samba4
+ */
+
+ ep_description.type = ENDPOINT_SMB;
+ ep_description.info.smb_pipe = p->pipe_name;
+
+ /* tell the RPC layer the session_info */
+ if (req->user_ctx->vuser) {
+ /*
+ * TODO: we need to reference count the entire session_info
+ */
+ session_info = req->user_ctx->vuser->session_info;
+ }
+
+ status = dcesrv_endpoint_search_connect(&req->smb_ctx->dcesrv,
+ &ep_description,
+ session_info,
+ &p->dce_conn);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_destroy(mem_ctx);
+ return status;
+ }
+
+ private->num_open++;
+
+ DLIST_ADD(private->pipe_list, p);
+
+ *ps = p;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ open a file with ntcreatex - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_ntcreatex(struct request_context *req, union smb_open *oi)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ status = ipc_open_generic(req, oi->ntcreatex.in.fname, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(oi->ntcreatex.out);
+ oi->ntcreatex.out.fnum = p->fnum;
+ oi->ntcreatex.out.ipc_state = p->ipc_state;
+
+ return status;
+}
+
+/*
+ open a file with openx - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open_openx(struct request_context *req, union smb_open *oi)
+{
+ struct pipe_state *p;
+ NTSTATUS status;
+ const char *fname = oi->openx.in.fname;
+
+ if (strncasecmp(fname, "PIPE\\", 5) != 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ fname += 4;
+
+ status = ipc_open_generic(req, fname, &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ZERO_STRUCT(oi->openx.out);
+ oi->openx.out.fnum = p->fnum;
+ oi->openx.out.ftype = 2;
+ oi->openx.out.devstate = p->ipc_state;
+
+ return status;
+}
+
/*
- open a file
+ open a file - used for MSRPC pipes
*/
static NTSTATUS ipc_open(struct request_context *req, union smb_open *oi)
{
- return NT_STATUS_ACCESS_DENIED;
+ NTSTATUS status;
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ status = ipc_open_ntcreatex(req, oi);
+ break;
+ case RAW_OPEN_OPENX:
+ status = ipc_open_openx(req, oi);
+ break;
+ default:
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ return status;
}
/*
*/
static NTSTATUS ipc_read(struct request_context *req, union smb_read *rd)
{
- return NT_STATUS_ACCESS_DENIED;
+ struct ipc_private *private = req->conn->ntvfs_private;
+ DATA_BLOB data;
+ uint16_t fnum;
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ switch (rd->generic.level) {
+ case RAW_READ_READ:
+ fnum = rd->read.in.fnum;
+ data.length = rd->read.in.count;
+ data.data = rd->read.out.data;
+ break;
+ case RAW_READ_READX:
+ fnum = rd->readx.in.fnum;
+ data.length = rd->readx.in.maxcnt;
+ data.data = rd->readx.out.data;
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ p = pipe_state_find(private, fnum);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = dcesrv_output_blob(p->dce_conn, &data);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ switch (rd->generic.level) {
+ case RAW_READ_READ:
+ rd->read.out.nread = data.length;
+ break;
+ case RAW_READ_READX:
+ rd->readx.out.remaining = 0;
+ rd->readx.out.compaction_mode = 0;
+ rd->readx.out.nread = data.length;
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return status;
}
/*
*/
static NTSTATUS ipc_write(struct request_context *req, union smb_write *wr)
{
- return NT_STATUS_ACCESS_DENIED;
+ struct ipc_private *private = req->conn->ntvfs_private;
+ DATA_BLOB data;
+ uint16_t fnum;
+ struct pipe_state *p;
+ NTSTATUS status;
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITE:
+ fnum = wr->write.in.fnum;
+ data.data = wr->write.in.data;
+ data.length = wr->write.in.count;
+ break;
+
+ case RAW_WRITE_WRITEX:
+ fnum = wr->writex.in.fnum;
+ data.data = wr->writex.in.data;
+ data.length = wr->writex.in.count;
+ break;
+
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ p = pipe_state_find(private, fnum);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ status = dcesrv_input(p->dce_conn, &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITE:
+ wr->write.out.nwritten = data.length;
+ break;
+ case RAW_WRITE_WRITEX:
+ wr->writex.out.nwritten = data.length;
+ wr->writex.out.remaining = 0;
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_OK;
}
/*
*/
static NTSTATUS ipc_close(struct request_context *req, union smb_close *io)
{
- return NT_STATUS_ACCESS_DENIED;
+ struct ipc_private *private = req->conn->ntvfs_private;
+ struct pipe_state *p;
+
+ if (io->generic.level != RAW_CLOSE_CLOSE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ p = pipe_state_find(private, io->close.in.fnum);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ pipe_shutdown(private, p);
+ private->num_open--;
+
+ return NT_STATUS_OK;
}
/*
}
+/* SMBtrans - handle a DCERPC command */
+static NTSTATUS ipc_dcerpc_cmd(struct request_context *req, struct smb_trans2 *trans)
+{
+ struct pipe_state *p;
+ struct ipc_private *private = req->conn->ntvfs_private;
+ NTSTATUS status;
+
+ /* the fnum is in setup[1] */
+ p = pipe_state_find(private, trans->in.setup[1]);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ trans->out.data = data_blob_talloc(req->mem_ctx, 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_blob(p->dce_conn, &trans->out.data);
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+
+ return status;
+}
+
+
+/* SMBtrans - set named pipe state */
+static NTSTATUS ipc_set_nm_pipe_state(struct request_context *req, struct smb_trans2 *trans)
+{
+ struct pipe_state *p;
+ struct ipc_private *private = req->conn->ntvfs_private;
+
+ /* the fnum is in setup[1] */
+ p = pipe_state_find(private, trans->in.setup[1]);
+ 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);
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob(NULL, 0);
+
+ return NT_STATUS_OK;
+}
+
+
+/* SMBtrans - used to provide access to SMB pipes */
+static NTSTATUS ipc_trans(struct request_context *req, struct smb_trans2 *trans)
+{
+ NTSTATUS status;
+
+ if (trans->in.setup_count != 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (trans->in.setup[0]) {
+ case TRANSACT_SETNAMEDPIPEHANDLESTATE:
+ status = ipc_set_nm_pipe_state(req, trans);
+ break;
+ case TRANSACT_DCERPCCMD:
+ status = ipc_dcerpc_cmd(req, trans);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ return status;
+}
+
+
+
/*
initialialise the IPC backend, registering ourselves with the ntvfs subsystem
*/
ZERO_STRUCT(ops);
- /* fill in all the operations */
- ops.name = "ipc";
+ /* fill in the name and type */
+ ops.name = "default";
ops.type = NTVFS_IPC;
+
+ /* fill in all the operations */
ops.connect = ipc_connect;
ops.disconnect = ipc_disconnect;
ops.unlink = ipc_unlink;
ops.search_first = ipc_search_first;
ops.search_next = ipc_search_next;
ops.search_close = ipc_search_close;
+ ops.trans = ipc_trans;
/* register ourselves with the NTVFS subsystem. */
ret = register_backend("ntvfs", &ops);