From ad6303f82fa862111c239b32b39f299e563a0802 Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Sat, 28 Jan 2006 12:58:38 +0000 Subject: r13208: Clearly separate named pipes from the IPC$ NTVFS type. This allows the easy addition of additional named pipes and removes the circular dependencies between the CIFS, RPC and RAP servers. Simple tests for a custom named pipe included. (This used to be commit 898d15acbd18e3b302a856c847e08c22c5024792) --- source4/build/smb_build/smb_build_h.pm | 5 +- source4/include/smb.h | 8 +- source4/include/structs.h | 2 + source4/librpc/rpc/dcerpc_smb.c | 2 +- source4/ntvfs/config.mk | 5 +- source4/ntvfs/ipc/ipc.h | 30 +++ source4/ntvfs/ipc/np_echo.c | 77 ++++++ source4/ntvfs/ipc/vfs_ipc.c | 243 ++++++++--------- source4/rpc_server/config.mk | 1 + source4/rpc_server/dcerpc_np.c | 167 ++++++++++++ source4/rpc_server/dcerpc_server.c | 19 +- source4/script/tests/test_quick.sh | 2 +- source4/torture/config.mk | 11 +- source4/torture/ipc/np_echo.c | 119 ++++++++ source4/torture/ipc/rap.c | 477 +++++++++++++++++++++++++++++++++ source4/torture/rap/rap.c | 477 --------------------------------- source4/torture/torture.c | 1 + 17 files changed, 1018 insertions(+), 628 deletions(-) create mode 100644 source4/ntvfs/ipc/ipc.h create mode 100644 source4/ntvfs/ipc/np_echo.c create mode 100644 source4/rpc_server/dcerpc_np.c create mode 100644 source4/torture/ipc/np_echo.c create mode 100644 source4/torture/ipc/rap.c delete mode 100644 source4/torture/rap/rap.c diff --git a/source4/build/smb_build/smb_build_h.pm b/source4/build/smb_build/smb_build_h.pm index 3b178c8226..d88f98040e 100644 --- a/source4/build/smb_build/smb_build_h.pm +++ b/source4/build/smb_build/smb_build_h.pm @@ -28,7 +28,10 @@ sub _prepare_build_h($) foreach my $key (values %{$depend}) { my $DEFINE = (); - next if ($key->{TYPE} ne "LIBRARY" and $key->{TYPE} ne "SUBSYSTEM"); + next if ($key->{TYPE} ne "LIBRARY" and + $key->{TYPE} ne "SUBSYSTEM" and + $key->{TYPE} ne "BINARY" and + $key->{TYPE} ne "MODULE"); next unless defined($key->{INIT_FUNCTIONS}); $DEFINE->{COMMENT} = "$key->{TYPE} $key->{NAME} INIT"; diff --git a/source4/include/smb.h b/source4/include/smb.h index 78a66dd20a..a1934c9635 100644 --- a/source4/include/smb.h +++ b/source4/include/smb.h @@ -319,10 +319,10 @@ /* used to indicate end of chain */ #define SMB_CHAIN_NONE 0xFF -/* These are the trans subcommands */ -#define TRANSACT_SETNAMEDPIPEHANDLESTATE 0x01 -#define TRANSACT_DCERPCCMD 0x26 -#define TRANSACT_WAITNAMEDPIPEHANDLESTATE 0x53 +/* These are the named pipe commands */ +#define NAMED_PIPE_SETHANDLESTATE 0x01 +#define NAMED_PIPE_TRANSACT 0x26 +#define NAMED_PIPE_WAITHANDLESTATE 0x53 /* These are the NT transact sub commands. */ #define NT_TRANSACT_CREATE 1 diff --git a/source4/include/structs.h b/source4/include/structs.h index 60ec1eae67..05167db5f9 100644 --- a/source4/include/structs.h +++ b/source4/include/structs.h @@ -164,3 +164,5 @@ struct smbcli_state; struct substitute_context; struct model_ops; + +struct named_pipe_ops; diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index c0716a1af8..8a7ce88ed5 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -240,7 +240,7 @@ static NTSTATUS smb_send_trans_request(struct dcerpc_connection *c, DATA_BLOB *b trans->in.data = *blob; trans->in.params = data_blob(NULL, 0); - setup[0] = TRANSACT_DCERPCCMD; + setup[0] = NAMED_PIPE_TRANSACT; setup[1] = smb->fnum; trans->in.max_param = 0; diff --git a/source4/ntvfs/config.mk b/source4/ntvfs/config.mk index 74b62809f7..df96998cfe 100644 --- a/source4/ntvfs/config.mk +++ b/source4/ntvfs/config.mk @@ -47,7 +47,10 @@ OBJ_FILES = \ # End MODULE ntvfs_ipc ################################################ - +[MODULE::np_echo] +INIT_FUNCTION = np_echo_init +OBJ_FILES = ipc/np_echo.o +SUBSYSTEM = ntvfs_ipc ################################################ # Start MODULE ntvfs_nbench diff --git a/source4/ntvfs/ipc/ipc.h b/source4/ntvfs/ipc/ipc.h new file mode 100644 index 0000000000..53818a5845 --- /dev/null +++ b/source4/ntvfs/ipc/ipc.h @@ -0,0 +1,30 @@ +/* + Unix SMB/CIFS implementation. + NTVFS IPC$ Named Pipes + Copyright (C) Jelmer Vernooij 2005 + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +struct named_pipe_ops { + NTSTATUS (*open)(void *context_data, + const char *path, + struct auth_session_info *session, + struct stream_connection *stream, + TALLOC_CTX *ctx, void **private_data); + NTSTATUS (*trans)(void *private_data, DATA_BLOB *in, DATA_BLOB *out); + NTSTATUS (*write)(void *private_data, DATA_BLOB *out); + NTSTATUS (*read)(void *private_data, DATA_BLOB *in); +}; diff --git a/source4/ntvfs/ipc/np_echo.c b/source4/ntvfs/ipc/np_echo.c new file mode 100644 index 0000000000..bfa0083b79 --- /dev/null +++ b/source4/ntvfs/ipc/np_echo.c @@ -0,0 +1,77 @@ +/* + Unix SMB/CIFS implementation. + DCE/RPC over named pipes support (glue between dcerpc and smb servers) + + Copyright (C) Jelmer Vernooij 2005 + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "rpc_server/dcerpc_server.h" +#include "ntvfs/ipc/ipc.h" + +static NTSTATUS echo_pipe_open (void *context_data, const char *path, struct auth_session_info *session_info, struct stream_connection *srv_conn, TALLOC_CTX *mem_ctx, void **private_data) +{ + *private_data = talloc_zero(mem_ctx, DATA_BLOB); + + return NT_STATUS_OK; +} + +static NTSTATUS echo_pipe_trans(void *private_data, DATA_BLOB *in, DATA_BLOB *out) +{ + memcpy(out->data, in->data, MIN(out->length,in->length)); + + return NT_STATUS_OK; +} + +static NTSTATUS echo_pipe_write(void *private_data, DATA_BLOB *out) +{ + DATA_BLOB *cache = private_data; + return data_blob_append(cache, cache, out->data, out->length); +} + +static NTSTATUS echo_pipe_read(void *private_data, DATA_BLOB *in) +{ + uint8_t *newdata; + DATA_BLOB *cache = private_data; + uint32_t numread = MIN(in->length, cache->length); + + memcpy(in->data, cache->data, numread); + + cache->length -= numread; + newdata = talloc_memdup(cache, cache+numread, cache->length); + if (newdata == NULL) + return NT_STATUS_NO_MEMORY; + + talloc_free(cache->data); + cache->data = newdata; + + return NT_STATUS_OK; +} + +const struct named_pipe_ops echo_pipe_ops = { + .open = echo_pipe_open, + .write = echo_pipe_write, + .read = echo_pipe_read, + .trans = echo_pipe_trans +}; + +NTSTATUS np_echo_init(void) +{ + return named_pipe_listen("\\PIPE\\NPECHO", &echo_pipe_ops, NULL); +} diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c index dd7994c1fb..bde87684e1 100644 --- a/source4/ntvfs/ipc/vfs_ipc.c +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -4,6 +4,7 @@ Copyright (C) Andrew Tridgell 2003 Copyright (C) Stefan (metze) Metzmacher 2004-2005 + Copyright (C) Jelmer Vernooij 2005 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 @@ -28,8 +29,10 @@ #include "includes.h" #include "dlinklist.h" #include "smb_server/smb_server.h" +#include "ntvfs/ipc/ipc.h" #include "ntvfs/ntvfs.h" #include "rpc_server/dcerpc_server.h" +#include "smb_build.h" #define IPC_BASE_FNUM 0x400 @@ -46,8 +49,9 @@ struct ipc_private { struct pipe_state *next, *prev; struct ipc_private *private; const char *pipe_name; + const struct named_pipe_ops *ops; + void *private_data; uint16_t fnum; - struct dcesrv_connection *dce_conn; uint16_t ipc_state; /* we need to remember the session it was opened on, as it is illegal to operate on someone elses fnum */ @@ -76,7 +80,6 @@ static struct pipe_state *pipe_state_find(struct ipc_private *private, uint16_t static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, const char *sharename) { - NTSTATUS status; struct smbsrv_tcon *tcon = req->tcon; struct ipc_private *private; @@ -97,10 +100,6 @@ static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs, private->idtree_fnum = idr_init(private); NT_STATUS_HAVE_NO_MEMORY(private->idtree_fnum); - /* setup the DCERPC server subsystem */ - status = dcesrv_init_ipc_context(private, &private->dcesrv); - NT_STATUS_NOT_OK_RETURN(status); - return NT_STATUS_OK; } @@ -171,6 +170,55 @@ static int ipc_fd_destructor(void *ptr) return 0; } +static struct named_pipe { + struct named_pipe *prev, *next; + const char *name; + const struct named_pipe_ops *ops; + void *context_data; +} *named_pipes = NULL; + +static NTSTATUS find_pipe_ops(const char *fname, const struct named_pipe_ops **ops, void **context_data) +{ + struct named_pipe *np; + + for (np = named_pipes; np; np = np->next) { + if (strcasecmp_m(np->name, fname) == 0) { + if (ops) *ops = np->ops; + if (context_data) *context_data = np->context_data; + return NT_STATUS_OK; + } + } + + return NT_STATUS_OBJECT_NAME_NOT_FOUND; +} + +NTSTATUS named_pipe_listen(const char *name, const struct named_pipe_ops *ops, void *context_data) +{ + NTSTATUS status; + struct named_pipe *np; + DEBUG(3, ("Registering named pipe `%s'\n", name)); + + status = find_pipe_ops(name, NULL, NULL); + if (NT_STATUS_IS_OK(status)) { + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return status; + } + + np = talloc(talloc_autofree_context(), struct named_pipe); + np->name = talloc_strdup(np, name); + np->ops = ops; + np->context_data = context_data; + np->prev = np->next = NULL; + + DLIST_ADD(named_pipes, np); + + return NT_STATUS_OK; +} + + /* open a file backend - used for MSRPC pipes @@ -181,10 +229,9 @@ static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs, { struct pipe_state *p; NTSTATUS status; - struct dcerpc_binding *ep_description; struct ipc_private *private = ntvfs->private_data; + void *context_data; int fnum; - struct stream_connection *srv_conn = req->smb_conn->connection; if (!req->session || !req->session->session_info) { return NT_STATUS_ACCESS_DENIED; @@ -193,14 +240,24 @@ static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs, p = talloc(req, 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++; - + p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname); NT_STATUS_HAVE_NO_MEMORY(p->pipe_name); + status = find_pipe_ops(p->pipe_name, &p->ops, &context_data); + + /* FIXME: Perhaps fall back to opening /var/lib/samba/ipc/ ? */ + if (NT_STATUS_IS_ERR(status)) { + DEBUG(0, ("Unable to find pipe ops for `%s'\n", p->pipe_name)); + return status; + } + + status = p->ops->open(context_data, p->pipe_name, req->session->session_info, req->smb_conn->connection, p, &p->private_data); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + fnum = idr_get_new_above(private->idtree_fnum, p, IPC_BASE_FNUM, UINT16_MAX); if (fnum == -1) { return NT_STATUS_TOO_MANY_OPENED_FILES; @@ -209,29 +266,6 @@ static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs, p->fnum = fnum; 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, - req->session->session_info, - srv_conn, - &p->dce_conn); - if (!NT_STATUS_IS_OK(status)) { - idr_remove(private->idtree_fnum, p->fnum); - return status; - } - DLIST_ADD(private->pipe_list, p); p->smbpid = req->smbpid; @@ -351,18 +385,6 @@ static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs, return NT_STATUS_ACCESS_DENIED; } -static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten) -{ - DATA_BLOB *blob = private_data; - - if (out->length < blob->length) { - blob->length = out->length; - } - memcpy(blob->data, out->data, blob->length); - *nwritten = blob->length; - return NT_STATUS_OK; -} - /* read from a file */ @@ -393,7 +415,7 @@ static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs, } if (data.length != 0) { - status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output); + status = p->ops->read(p->private_data, &data); if (NT_STATUS_IS_ERR(status)) { return status; } @@ -431,7 +453,7 @@ static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_HANDLE; } - status = dcesrv_input(p->dce_conn, &data); + status = p->ops->write(p->private_data, &data); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -617,30 +639,12 @@ static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs, 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; - } - - if (out->length < blob->length) { - blob->length = out->length; - } - memcpy(blob->data, out->data, blob->length); - *nwritten = blob->length; - return status; -} - -/* SMBtrans - handle a DCERPC command */ -static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs, - struct smbsrv_request *req, struct smb_trans2 *trans) +/* SMBtrans - set named pipe state */ +static NTSTATUS ipc_np_set_nm_state(struct ntvfs_module_context *ntvfs, + struct smbsrv_request *req, struct smb_trans2 *trans) { - struct pipe_state *p; struct ipc_private *private = ntvfs->private_data; - NTSTATUS status; + struct pipe_state *p; /* the fnum is in setup[1] */ p = pipe_state_find(private, trans->in.setup[1]); @@ -648,43 +652,25 @@ static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs, 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; + 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 status; + return NT_STATUS_OK; } - -/* SMBtrans - set named pipe state */ -static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs, +/* SMBtrans - named pipe transaction */ +static NTSTATUS ipc_np_trans(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_trans2 *trans) { struct ipc_private *private = ntvfs->private_data; + NTSTATUS status; struct pipe_state *p; /* the fnum is in setup[1] */ @@ -693,50 +679,55 @@ static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_HANDLE; } - if (trans->in.params.length != 2) { + if (trans->in.setup_count != 2) { return NT_STATUS_INVALID_PARAMETER; } - p->ipc_state = SVAL(trans->in.params.data, 0); + + trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data); + if (!trans->out.data.data) { + return NT_STATUS_NO_MEMORY; + } + + status = p->ops->trans(p->private_data, &trans->in.data, &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); - trans->out.data = data_blob(NULL, 0); return NT_STATUS_OK; } - /* SMBtrans - used to provide access to SMB pipes */ static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs, struct smbsrv_request *req, struct smb_trans2 *trans) { NTSTATUS status; - if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN")) - return ipc_rap_call(req, trans); - - 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(ntvfs, req, trans); - break; - case TRANSACT_DCERPCCMD: - status = ipc_dcerpc_cmd(ntvfs, req, trans); - break; - default: - status = NT_STATUS_INVALID_PARAMETER; - break; + if (strequal(trans->in.trans_name, "\\PIPE\\")) { /* Named pipe */ + switch (trans->in.setup[0]) { + case NAMED_PIPE_SETHANDLESTATE: + status = ipc_np_set_nm_state(ntvfs, req, trans); + break; + case NAMED_PIPE_TRANSACT: + status = ipc_np_trans(ntvfs, req, trans); + break; + default: + status = NT_STATUS_INVALID_PARAMETER; + break; + } + } else if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN")) { /* RAP */ + status = ipc_rap_call(req, trans); + } else { + DEBUG(1, ("Unknown transaction name `%s'\n", trans->in.trans_name)); + status = NT_STATUS_NOT_SUPPORTED; } return status; } - - /* initialialise the IPC backend, registering ourselves with the ntvfs subsystem */ @@ -744,7 +735,9 @@ NTSTATUS ntvfs_ipc_init(void) { NTSTATUS ret; struct ntvfs_ops ops; - + init_module_fn static_init[] = STATIC_ntvfs_ipc_MODULES; + init_module_fn *shared_init; + ZERO_STRUCT(ops); /* fill in the name and type */ @@ -791,5 +784,13 @@ NTSTATUS ntvfs_ipc_init(void) return ret; } + /* load available named pipe backends */ + shared_init = load_samba_modules(NULL, "np"); + + run_init_functions(static_init); + run_init_functions(shared_init); + + talloc_free(shared_init); + return ret; } diff --git a/source4/rpc_server/config.mk b/source4/rpc_server/config.mk index 73f61602bd..4aeb4f79c7 100644 --- a/source4/rpc_server/config.mk +++ b/source4/rpc_server/config.mk @@ -204,6 +204,7 @@ REQUIRED_SUBSYSTEMS = \ PRIVATE_PROTO_HEADER = dcerpc_server_proto.h OBJ_FILES = \ dcerpc_server.o \ + dcerpc_np.o \ dcerpc_sock.o \ dcesrv_auth.o \ handles.o diff --git a/source4/rpc_server/dcerpc_np.c b/source4/rpc_server/dcerpc_np.c new file mode 100644 index 0000000000..a1da60f7f6 --- /dev/null +++ b/source4/rpc_server/dcerpc_np.c @@ -0,0 +1,167 @@ +/* + Unix SMB/CIFS implementation. + DCE/RPC over named pipes support (glue between dcerpc and smb servers) + + Copyright (C) Jelmer Vernooij 2005 + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "lib/socket/socket.h" +#include "lib/events/events.h" +#include "rpc_server/dcerpc_server.h" +#include "ntvfs/ipc/ipc.h" + +static NTSTATUS dcesrv_pipe_open (void *context_data, const char *path, struct auth_session_info *session_info, struct stream_connection *srv_conn, TALLOC_CTX *mem_ctx, void **private_data) +{ + NTSTATUS status; + struct dcerpc_binding *ep_description; + struct dcesrv_connection *dce_conn; + + ep_description = talloc(mem_ctx, struct dcerpc_binding); + NT_STATUS_HAVE_NO_MEMORY(ep_description); + + /* + 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, path); + + /* The session info is refcount-increased in the + * dcesrv_endpoint_search_connect() function + */ + status = dcesrv_endpoint_search_connect(context_data, + mem_ctx, + ep_description, + session_info, + srv_conn, + &dce_conn); + talloc_free(ep_description); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *private_data = dce_conn; + + return NT_STATUS_OK; +} + +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; + } + + if (out->length < blob->length) { + blob->length = out->length; + } + memcpy(blob->data, out->data, blob->length); + *nwritten = blob->length; + return status; +} + + +static NTSTATUS dcesrv_pipe_trans(void *private_data, DATA_BLOB *in, DATA_BLOB *out) +{ + struct dcesrv_connection *dce_conn = private_data; + NTSTATUS status; + + /* 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(dce_conn, in); + 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(dce_conn, out, ipc_trans_dcesrv_output); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + return status; +} + +static NTSTATUS dcesrv_pipe_write(void *private_data, DATA_BLOB *out) +{ + struct dcesrv_connection *dce_conn = private_data; + NTSTATUS status; + + status = dcesrv_input(dce_conn, out); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return status; +} + +static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten) +{ + DATA_BLOB *blob = private_data; + + if (out->length < blob->length) { + blob->length = out->length; + } + memcpy(blob->data, out->data, blob->length); + *nwritten = blob->length; + return NT_STATUS_OK; +} + + +static NTSTATUS dcesrv_pipe_read(void *private_data, DATA_BLOB *in) +{ + struct dcesrv_connection *dce_conn = private_data; + NTSTATUS status; + + status = dcesrv_output(dce_conn, in, ipc_readx_dcesrv_output); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + return status; +} + +const struct named_pipe_ops dce_pipe_ops = { + .open = dcesrv_pipe_open, + .write = dcesrv_pipe_write, + .read = dcesrv_pipe_read, + .trans = dcesrv_pipe_trans +}; + +/* Add named pipe endpoint */ +NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e, struct event_context *event_ctx, const struct model_ops *model_ops) +{ + NTSTATUS status; + + status = named_pipe_listen(e->ep_description->endpoint, &dce_pipe_ops, dce_ctx); + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + return NT_STATUS_OK; +} diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index 2aeb737f5e..0c50c4aaaa 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -1171,21 +1171,6 @@ static NTSTATUS dcesrv_init_context(TALLOC_CTX *mem_ctx, const char **endpoint_s return NT_STATUS_OK; } -/* - initialise the dcerpc server context for ncacn_np based services -*/ -NTSTATUS dcesrv_init_ipc_context(TALLOC_CTX *mem_ctx, struct dcesrv_context **_dce_ctx) -{ - NTSTATUS status; - struct dcesrv_context *dce_ctx; - - status = dcesrv_init_context(mem_ctx, lp_dcerpc_endpoint_servers(), 0, &dce_ctx); - NT_STATUS_NOT_OK_RETURN(status); - - *_dce_ctx = dce_ctx; - return NT_STATUS_OK; -} - /* the list of currently registered DCERPC endpoint servers. */ static struct ep_server { @@ -1305,8 +1290,8 @@ static NTSTATUS dcesrv_init(struct event_context *event_context, const struct mo break; case NCACN_NP: -/* FIXME: status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops); - NT_STATUS_NOT_OK_RETURN(status); */ + status = dcesrv_add_ep_np(dce_ctx, e, event_context, model_ops); + NT_STATUS_NOT_OK_RETURN(status); break; default: diff --git a/source4/script/tests/test_quick.sh b/source4/script/tests/test_quick.sh index 6c8e77a151..8e1c138c8f 100755 --- a/source4/script/tests/test_quick.sh +++ b/source4/script/tests/test_quick.sh @@ -23,7 +23,7 @@ tests="$tests BASE-DIR2 BASE-TCON BASE-OPEN" tests="$tests BASE-CHKPATH RAW-QFSINFO RAW-QFILEINFO RAW-SFILEINFO" tests="$tests RAW-LOCK RAW-MKDIR RAW-SEEK RAW-OPEN RAW-WRITE" tests="$tests RAW-UNLINK RAW-READ RAW-CLOSE RAW-IOCTL RAW-RENAME" -tests="$tests RAW-EAS RAW-STREAMS RAW-ACLS" +tests="$tests RAW-EAS RAW-STREAMS RAW-ACLS RAW-PIPE-ECHO" failed=0 for t in $tests; do diff --git a/source4/torture/config.mk b/source4/torture/config.mk index c7e8e2cc48..8d022cdce2 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -121,13 +121,14 @@ REQUIRED_SUBSYSTEMS = \ ################################# ################################# -# Start SUBSYSTEM TORTURE_RAP -[SUBSYSTEM::TORTURE_RAP] +# Start SUBSYSTEM TORTURE_IPC +[SUBSYSTEM::TORTURE_IPC] OBJ_FILES = \ - rap/rap.o + ipc/rap.o \ + ipc/np_echo.o REQUIRED_SUBSYSTEMS = \ LIBSMB -# End SUBSYSTEM TORTURE_RAP +# End SUBSYSTEM TORTURE_IPC ################################# ################################# @@ -210,7 +211,7 @@ REQUIRED_SUBSYSTEMS = \ TORTURE_RAW \ TORTURE_SMB2 \ TORTURE_RPC \ - TORTURE_RAP \ + TORTURE_IPC \ TORTURE_AUTH \ TORTURE_LOCAL \ TORTURE_NBENCH \ diff --git a/source4/torture/ipc/np_echo.c b/source4/torture/ipc/np_echo.c new file mode 100644 index 0000000000..7d81d4fd37 --- /dev/null +++ b/source4/torture/ipc/np_echo.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + Named Pipe Echo test + Copyright (C) Jelmer Vernooij 2005 + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/security.h" +#include "smb.h" +#include "torture/torture.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" + +#define ECHODATA "Good Times, Bad Times" + +int torture_np_echo(void) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct smbcli_state *cli; + const char *pipe_name = "\\NPECHO"; + union smb_open open; + union smb_read read; + union smb_write write; + union smb_close close; + int fnum; + BOOL ret; + + ret = torture_open_connection_share(mem_ctx, &cli, + lp_parm_string(-1, "torture", "host"), + "IPC$", + NULL); + if (!ret) + return False; + + open.ntcreatex.level = RAW_OPEN_NTCREATEX; + open.ntcreatex.in.flags = 0; + open.ntcreatex.in.root_fid = 0; + open.ntcreatex.in.access_mask = + SEC_STD_READ_CONTROL | + SEC_FILE_WRITE_ATTRIBUTE | + SEC_FILE_WRITE_EA | + SEC_FILE_READ_DATA | + SEC_FILE_WRITE_DATA; + open.ntcreatex.in.file_attr = 0; + open.ntcreatex.in.alloc_size = 0; + open.ntcreatex.in.share_access = + NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE; + open.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN; + open.ntcreatex.in.create_options = 0; + open.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION; + open.ntcreatex.in.security_flags = 0; + open.ntcreatex.in.fname = pipe_name; + + status = smb_raw_open(cli->tree, cli->tree, &open); + if (NT_STATUS_IS_ERR(status)) + return False; + + fnum = open.ntcreatex.out.fnum; + + write.write.level = RAW_WRITE_WRITE; + write.write.in.fnum = fnum; + write.write.in.count = strlen(ECHODATA); + write.write.in.offset = 0; + write.write.in.remaining = 0; + write.write.in.data = (const uint8_t *)ECHODATA; + + status = smb_raw_write(cli->tree, &write); + if (NT_STATUS_IS_ERR(status)) + return False; + + if (write.write.out.nwritten != strlen(ECHODATA)) + return False; + + read.read.level = RAW_READ_READ; + read.read.in.fnum = fnum; + read.read.in.count = strlen(ECHODATA); + read.read.in.offset = 0; + read.read.in.remaining = 0; + read.read.out.data = talloc_array(mem_ctx, uint8_t, strlen(ECHODATA)); + + status = smb_raw_read(cli->tree, &read); + + if (NT_STATUS_IS_ERR(status)) + return False; + + if (read.read.out.nread != strlen(ECHODATA)) + return False; + + if (memcmp(read.read.out.data, ECHODATA, strlen(ECHODATA)) != 0) { + printf ("np_echo: Returned data did not match!\n"); + return False; + } + + close.close.level = RAW_CLOSE_CLOSE; + close.close.in.fnum = fnum; + close.close.in.write_time = 0; + + status = smb_raw_close(cli->tree, &close); + if (NT_STATUS_IS_ERR(status)) + return False; + + return True; +} diff --git a/source4/torture/ipc/rap.c b/source4/torture/ipc/rap.c new file mode 100644 index 0000000000..24cf1bb642 --- /dev/null +++ b/source4/torture/ipc/rap.c @@ -0,0 +1,477 @@ +/* + Unix SMB/CIFS implementation. + test suite for various RAP operations + Copyright (C) Volker Lendecke 2004 + Copyright (C) Tim Potter 2005 + + 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 + (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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "rap.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/libcli.h" +#include "torture/torture.h" + +struct rap_call { + uint16_t callno; + char *paramdesc; + const char *datadesc; + + uint16_t status; + uint16_t convert; + + uint16_t rcv_paramlen, rcv_datalen; + + struct ndr_push *ndr_push_param; + struct ndr_push *ndr_push_data; + struct ndr_pull *ndr_pull_param; + struct ndr_pull *ndr_pull_data; +}; + +#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM); + +static struct rap_call *new_rap_cli_call(TALLOC_CTX *mem_ctx, uint16_t callno) +{ + struct rap_call *call; + + call = talloc(mem_ctx, struct rap_call); + + if (call == NULL) + return NULL; + + call->callno = callno; + call->rcv_paramlen = 4; + + call->paramdesc = NULL; + call->datadesc = NULL; + + call->ndr_push_param = ndr_push_init_ctx(mem_ctx); + call->ndr_push_param->flags = RAPNDR_FLAGS; + + call->ndr_push_data = ndr_push_init_ctx(mem_ctx); + call->ndr_push_data->flags = RAPNDR_FLAGS; + + return call; +} + +static void rap_cli_push_paramdesc(struct rap_call *call, char desc) +{ + int len = 0; + + if (call->paramdesc != NULL) + len = strlen(call->paramdesc); + + call->paramdesc = talloc_realloc(call, + call->paramdesc, + uint8_t, + len+2); + + call->paramdesc[len] = desc; + call->paramdesc[len+1] = '\0'; +} + +static void rap_cli_push_word(struct rap_call *call, uint16_t val) +{ + rap_cli_push_paramdesc(call, 'W'); + ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, val); +} + +static void rap_cli_push_dword(struct rap_call *call, uint32_t val) +{ + rap_cli_push_paramdesc(call, 'D'); + ndr_push_uint32(call->ndr_push_param, NDR_SCALARS, val); +} + +static void rap_cli_push_rcvbuf(struct rap_call *call, int len) +{ + rap_cli_push_paramdesc(call, 'r'); + rap_cli_push_paramdesc(call, 'L'); + ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, len); + call->rcv_datalen = len; +} + +static void rap_cli_expect_multiple_entries(struct rap_call *call) +{ + rap_cli_push_paramdesc(call, 'e'); + rap_cli_push_paramdesc(call, 'h'); + call->rcv_paramlen += 4; /* uint16_t entry count, uint16_t total */ +} + +static void rap_cli_push_string(struct rap_call *call, const char *str) +{ + if (str == NULL) { + rap_cli_push_paramdesc(call, 'O'); + return; + } + rap_cli_push_paramdesc(call, 'z'); + ndr_push_string(call->ndr_push_param, NDR_SCALARS, str); +} + +static void rap_cli_expect_format(struct rap_call *call, const char *format) +{ + call->datadesc = format; +} + +static NTSTATUS rap_pull_string(TALLOC_CTX *mem_ctx, struct ndr_pull *ndr, + uint16_t convert, char **dest) +{ + uint16_t string_offset; + uint16_t ignore; + const char *p; + size_t len; + + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &string_offset)); + NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &ignore)); + + string_offset -= convert; + + if (string_offset+1 > ndr->data_size) + return NT_STATUS_INVALID_PARAMETER; + + p = (const char *)(ndr->data + string_offset); + len = strnlen(p, ndr->data_size-string_offset); + + if ( string_offset + len + 1 > ndr->data_size ) + return NT_STATUS_INVALID_PARAMETER; + + *dest = talloc_zero_size(mem_ctx, len+1); + pull_ascii(*dest, p, len+1, len, 0); + + return NT_STATUS_OK; +} + +static NTSTATUS rap_cli_do_call(struct smbcli_state *cli, struct rap_call *call) +{ + NTSTATUS result; + DATA_BLOB param_blob; + struct ndr_push *params; + struct smb_trans2 trans; + + params = ndr_push_init_ctx(call); + + if (params == NULL) + return NT_STATUS_NO_MEMORY; + + params->flags = RAPNDR_FLAGS; + + trans.in.max_param = call->rcv_paramlen; + trans.in.max_data = smb_raw_max_trans_data(cli->tree, call->rcv_paramlen); + trans.in.max_setup = 0; + trans.in.flags = 0; + trans.in.timeout = 0; + trans.in.setup_count = 0; + trans.in.setup = NULL; + trans.in.trans_name = "\\PIPE\\LANMAN"; + + NDR_CHECK(ndr_push_uint16(params, NDR_SCALARS, call->callno)); + if (call->paramdesc) + NDR_CHECK(ndr_push_string(params, NDR_SCALARS, call->paramdesc)); + if (call->datadesc) + NDR_CHECK(ndr_push_string(params, NDR_SCALARS, call->datadesc)); + + param_blob = ndr_push_blob(call->ndr_push_param); + NDR_CHECK(ndr_push_bytes(params, param_blob.data, + param_blob.length)); + + trans.in.params = ndr_push_blob(params); + trans.in.data = data_blob(NULL, 0); + + result = smb_raw_trans(cli->tree, call, &trans); + + if (!NT_STATUS_IS_OK(result)) + return result; + + call->ndr_pull_param = ndr_pull_init_blob(&trans.out.params, call); + call->ndr_pull_param->flags = RAPNDR_FLAGS; + + call->ndr_pull_data = ndr_pull_init_blob(&trans.out.data, call); + call->ndr_pull_data->flags = RAPNDR_FLAGS; + + return result; +} + +#define NDR_OK(call) do { NTSTATUS _status; \ + _status = call; \ + if (!NT_STATUS_IS_OK(_status)) \ + goto done; \ + } while (0) + +static NTSTATUS smbcli_rap_netshareenum(struct smbcli_state *cli, + TALLOC_CTX *mem_ctx, + struct rap_NetShareEnum *r) +{ + struct rap_call *call; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + int i; + + call = new_rap_cli_call(NULL, RAP_WshareEnum); + + if (call == NULL) + return NT_STATUS_NO_MEMORY; + + rap_cli_push_word(call, r->in.level); /* Level */ + rap_cli_push_rcvbuf(call, r->in.bufsize); + rap_cli_expect_multiple_entries(call); + + switch(r->in.level) { + case 0: + rap_cli_expect_format(call, "B13"); + break; + case 1: + rap_cli_expect_format(call, "B13BWz"); + break; + } + + result = rap_cli_do_call(cli, call); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available)); + + r->out.info = talloc_array(mem_ctx, union rap_shareenum_info, r->out.count); + + if (r->out.info == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; iout.count; i++) { + switch(r->in.level) { + case 0: + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + (uint8_t *)r->out.info[i].info0.name, 13)); + break; + case 1: + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + (uint8_t *)r->out.info[i].info1.name, 13)); + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + (uint8_t *)&r->out.info[i].info1.pad, 1)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_data, + NDR_SCALARS, &r->out.info[i].info1.type)); + NDR_OK(rap_pull_string(mem_ctx, call->ndr_pull_data, + r->out.convert, + &r->out.info[i].info1.comment)); + break; + } + } + + result = NT_STATUS_OK; + + done: + talloc_free(call); + return result; +} + +static BOOL test_netshareenum(struct smbcli_state *cli) +{ + struct rap_NetShareEnum r; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(cli); + + r.in.level = 1; + r.in.bufsize = 8192; + + if (!NT_STATUS_IS_OK(smbcli_rap_netshareenum(cli, tmp_ctx, &r))) + return False; + + for (i=0; iin.level); + rap_cli_push_rcvbuf(call, r->in.bufsize); + rap_cli_expect_multiple_entries(call); + rap_cli_push_dword(call, r->in.servertype); + rap_cli_push_string(call, r->in.domain); + + switch(r->in.level) { + case 0: + rap_cli_expect_format(call, "B16"); + break; + case 1: + rap_cli_expect_format(call, "B16BBDz"); + break; + } + + result = rap_cli_do_call(cli, call); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = NT_STATUS_INVALID_PARAMETER; + + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count)); + NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available)); + + r->out.info = talloc_array(mem_ctx, union rap_server_info, r->out.count); + + if (r->out.info == NULL) { + result = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; iout.count; i++) { + switch(r->in.level) { + case 0: + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + (uint8_t *)r->out.info[i].info0.name, 16)); + break; + case 1: + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + (uint8_t *)r->out.info[i].info1.name, 16)); + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + &r->out.info[i].info1.version_major, 1)); + NDR_OK(ndr_pull_bytes(call->ndr_pull_data, + &r->out.info[i].info1.version_minor, 1)); + NDR_OK(ndr_pull_uint32(call->ndr_pull_data, + NDR_SCALARS, &r->out.info[i].info1.servertype)); + NDR_OK(rap_pull_string(mem_ctx, call->ndr_pull_data, + r->out.convert, + &r->out.info[i].info1.comment)); + } + } + + result = NT_STATUS_OK; + + done: + talloc_free(call); + return result; +} + +static BOOL test_netserverenum(struct smbcli_state *cli) +{ + struct rap_NetServerEnum2 r; + int i; + TALLOC_CTX *tmp_ctx = talloc_new(cli); + + r.in.level = 0; + r.in.bufsize = 8192; + r.in.servertype = 0xffffffff; + r.in.servertype = 0x80000000; + r.in.domain = NULL; + + if (!NT_STATUS_IS_OK(smbcli_rap_netserverenum2(cli, tmp_ctx, &r))) + return False; + + for (i=0; icallno = callno; - call->rcv_paramlen = 4; - - call->paramdesc = NULL; - call->datadesc = NULL; - - call->ndr_push_param = ndr_push_init_ctx(mem_ctx); - call->ndr_push_param->flags = RAPNDR_FLAGS; - - call->ndr_push_data = ndr_push_init_ctx(mem_ctx); - call->ndr_push_data->flags = RAPNDR_FLAGS; - - return call; -} - -static void rap_cli_push_paramdesc(struct rap_call *call, char desc) -{ - int len = 0; - - if (call->paramdesc != NULL) - len = strlen(call->paramdesc); - - call->paramdesc = talloc_realloc(call, - call->paramdesc, - uint8_t, - len+2); - - call->paramdesc[len] = desc; - call->paramdesc[len+1] = '\0'; -} - -static void rap_cli_push_word(struct rap_call *call, uint16_t val) -{ - rap_cli_push_paramdesc(call, 'W'); - ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, val); -} - -static void rap_cli_push_dword(struct rap_call *call, uint32_t val) -{ - rap_cli_push_paramdesc(call, 'D'); - ndr_push_uint32(call->ndr_push_param, NDR_SCALARS, val); -} - -static void rap_cli_push_rcvbuf(struct rap_call *call, int len) -{ - rap_cli_push_paramdesc(call, 'r'); - rap_cli_push_paramdesc(call, 'L'); - ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, len); - call->rcv_datalen = len; -} - -static void rap_cli_expect_multiple_entries(struct rap_call *call) -{ - rap_cli_push_paramdesc(call, 'e'); - rap_cli_push_paramdesc(call, 'h'); - call->rcv_paramlen += 4; /* uint16_t entry count, uint16_t total */ -} - -static void rap_cli_push_string(struct rap_call *call, const char *str) -{ - if (str == NULL) { - rap_cli_push_paramdesc(call, 'O'); - return; - } - rap_cli_push_paramdesc(call, 'z'); - ndr_push_string(call->ndr_push_param, NDR_SCALARS, str); -} - -static void rap_cli_expect_format(struct rap_call *call, const char *format) -{ - call->datadesc = format; -} - -static NTSTATUS rap_pull_string(TALLOC_CTX *mem_ctx, struct ndr_pull *ndr, - uint16_t convert, char **dest) -{ - uint16_t string_offset; - uint16_t ignore; - const char *p; - size_t len; - - NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &string_offset)); - NDR_CHECK(ndr_pull_uint16(ndr, NDR_SCALARS, &ignore)); - - string_offset -= convert; - - if (string_offset+1 > ndr->data_size) - return NT_STATUS_INVALID_PARAMETER; - - p = (const char *)(ndr->data + string_offset); - len = strnlen(p, ndr->data_size-string_offset); - - if ( string_offset + len + 1 > ndr->data_size ) - return NT_STATUS_INVALID_PARAMETER; - - *dest = talloc_zero_size(mem_ctx, len+1); - pull_ascii(*dest, p, len+1, len, 0); - - return NT_STATUS_OK; -} - -static NTSTATUS rap_cli_do_call(struct smbcli_state *cli, struct rap_call *call) -{ - NTSTATUS result; - DATA_BLOB param_blob; - struct ndr_push *params; - struct smb_trans2 trans; - - params = ndr_push_init_ctx(call); - - if (params == NULL) - return NT_STATUS_NO_MEMORY; - - params->flags = RAPNDR_FLAGS; - - trans.in.max_param = call->rcv_paramlen; - trans.in.max_data = smb_raw_max_trans_data(cli->tree, call->rcv_paramlen); - trans.in.max_setup = 0; - trans.in.flags = 0; - trans.in.timeout = 0; - trans.in.setup_count = 0; - trans.in.setup = NULL; - trans.in.trans_name = "\\PIPE\\LANMAN"; - - NDR_CHECK(ndr_push_uint16(params, NDR_SCALARS, call->callno)); - if (call->paramdesc) - NDR_CHECK(ndr_push_string(params, NDR_SCALARS, call->paramdesc)); - if (call->datadesc) - NDR_CHECK(ndr_push_string(params, NDR_SCALARS, call->datadesc)); - - param_blob = ndr_push_blob(call->ndr_push_param); - NDR_CHECK(ndr_push_bytes(params, param_blob.data, - param_blob.length)); - - trans.in.params = ndr_push_blob(params); - trans.in.data = data_blob(NULL, 0); - - result = smb_raw_trans(cli->tree, call, &trans); - - if (!NT_STATUS_IS_OK(result)) - return result; - - call->ndr_pull_param = ndr_pull_init_blob(&trans.out.params, call); - call->ndr_pull_param->flags = RAPNDR_FLAGS; - - call->ndr_pull_data = ndr_pull_init_blob(&trans.out.data, call); - call->ndr_pull_data->flags = RAPNDR_FLAGS; - - return result; -} - -#define NDR_OK(call) do { NTSTATUS _status; \ - _status = call; \ - if (!NT_STATUS_IS_OK(_status)) \ - goto done; \ - } while (0) - -static NTSTATUS smbcli_rap_netshareenum(struct smbcli_state *cli, - TALLOC_CTX *mem_ctx, - struct rap_NetShareEnum *r) -{ - struct rap_call *call; - NTSTATUS result = NT_STATUS_UNSUCCESSFUL; - int i; - - call = new_rap_cli_call(NULL, RAP_WshareEnum); - - if (call == NULL) - return NT_STATUS_NO_MEMORY; - - rap_cli_push_word(call, r->in.level); /* Level */ - rap_cli_push_rcvbuf(call, r->in.bufsize); - rap_cli_expect_multiple_entries(call); - - switch(r->in.level) { - case 0: - rap_cli_expect_format(call, "B13"); - break; - case 1: - rap_cli_expect_format(call, "B13BWz"); - break; - } - - result = rap_cli_do_call(cli, call); - - if (!NT_STATUS_IS_OK(result)) - goto done; - - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available)); - - r->out.info = talloc_array(mem_ctx, union rap_shareenum_info, r->out.count); - - if (r->out.info == NULL) { - result = NT_STATUS_NO_MEMORY; - goto done; - } - - for (i=0; iout.count; i++) { - switch(r->in.level) { - case 0: - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - (uint8_t *)r->out.info[i].info0.name, 13)); - break; - case 1: - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - (uint8_t *)r->out.info[i].info1.name, 13)); - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - (uint8_t *)&r->out.info[i].info1.pad, 1)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_data, - NDR_SCALARS, &r->out.info[i].info1.type)); - NDR_OK(rap_pull_string(mem_ctx, call->ndr_pull_data, - r->out.convert, - &r->out.info[i].info1.comment)); - break; - } - } - - result = NT_STATUS_OK; - - done: - talloc_free(call); - return result; -} - -static BOOL test_netshareenum(struct smbcli_state *cli) -{ - struct rap_NetShareEnum r; - int i; - TALLOC_CTX *tmp_ctx = talloc_new(cli); - - r.in.level = 1; - r.in.bufsize = 8192; - - if (!NT_STATUS_IS_OK(smbcli_rap_netshareenum(cli, tmp_ctx, &r))) - return False; - - for (i=0; iin.level); - rap_cli_push_rcvbuf(call, r->in.bufsize); - rap_cli_expect_multiple_entries(call); - rap_cli_push_dword(call, r->in.servertype); - rap_cli_push_string(call, r->in.domain); - - switch(r->in.level) { - case 0: - rap_cli_expect_format(call, "B16"); - break; - case 1: - rap_cli_expect_format(call, "B16BBDz"); - break; - } - - result = rap_cli_do_call(cli, call); - - if (!NT_STATUS_IS_OK(result)) - goto done; - - result = NT_STATUS_INVALID_PARAMETER; - - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.status)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.convert)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.count)); - NDR_OK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &r->out.available)); - - r->out.info = talloc_array(mem_ctx, union rap_server_info, r->out.count); - - if (r->out.info == NULL) { - result = NT_STATUS_NO_MEMORY; - goto done; - } - - for (i=0; iout.count; i++) { - switch(r->in.level) { - case 0: - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - (uint8_t *)r->out.info[i].info0.name, 16)); - break; - case 1: - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - (uint8_t *)r->out.info[i].info1.name, 16)); - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - &r->out.info[i].info1.version_major, 1)); - NDR_OK(ndr_pull_bytes(call->ndr_pull_data, - &r->out.info[i].info1.version_minor, 1)); - NDR_OK(ndr_pull_uint32(call->ndr_pull_data, - NDR_SCALARS, &r->out.info[i].info1.servertype)); - NDR_OK(rap_pull_string(mem_ctx, call->ndr_pull_data, - r->out.convert, - &r->out.info[i].info1.comment)); - } - } - - result = NT_STATUS_OK; - - done: - talloc_free(call); - return result; -} - -static BOOL test_netserverenum(struct smbcli_state *cli) -{ - struct rap_NetServerEnum2 r; - int i; - TALLOC_CTX *tmp_ctx = talloc_new(cli); - - r.in.level = 0; - r.in.bufsize = 8192; - r.in.servertype = 0xffffffff; - r.in.servertype = 0x80000000; - r.in.domain = NULL; - - if (!NT_STATUS_IS_OK(smbcli_rap_netserverenum2(cli, tmp_ctx, &r))) - return False; - - for (i=0; i