summaryrefslogtreecommitdiffstats
path: root/source/librpc/rpc
diff options
context:
space:
mode:
authorCVS Import User <samba-bugs@samba.org>2004-04-04 11:51:10 +0000
committerCVS Import User <samba-bugs@samba.org>2004-04-04 11:51:10 +0000
commite3d2dbdff6711b0bc768fb6b08f41240f21d5fba (patch)
tree159ef54b59b18e9b950f5c6af105915214244912 /source/librpc/rpc
parent139b1658ca30692835c1a7203c7cd003e587ac12 (diff)
downloadsamba-e3d2dbdff6711b0bc768fb6b08f41240f21d5fba.tar.gz
samba-e3d2dbdff6711b0bc768fb6b08f41240f21d5fba.tar.xz
samba-e3d2dbdff6711b0bc768fb6b08f41240f21d5fba.zip
r6: merge in the samba4 HEAD branch from cvs
to checkout try: svn co svn+ssh://svn.samba.org/home/svn/samba/branches/SAMBA_4_0 metze
Diffstat (limited to 'source/librpc/rpc')
-rw-r--r--source/librpc/rpc/dcerpc.c860
-rw-r--r--source/librpc/rpc/dcerpc.h117
-rw-r--r--source/librpc/rpc/dcerpc_auth.c43
-rw-r--r--source/librpc/rpc/dcerpc_lsa.c79
-rw-r--r--source/librpc/rpc/dcerpc_ntlm.c197
-rw-r--r--source/librpc/rpc/dcerpc_schannel.c266
-rw-r--r--source/librpc/rpc/dcerpc_smb.c391
-rw-r--r--source/librpc/rpc/dcerpc_tcp.c206
-rw-r--r--source/librpc/rpc/dcerpc_util.c668
9 files changed, 2827 insertions, 0 deletions
diff --git a/source/librpc/rpc/dcerpc.c b/source/librpc/rpc/dcerpc.c
new file mode 100644
index 00000000000..8987cead92f
--- /dev/null
+++ b/source/librpc/rpc/dcerpc.c
@@ -0,0 +1,860 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw dcerpc operations
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/* initialise a dcerpc pipe. This currently assumes a SMB named pipe
+ transport */
+struct dcerpc_pipe *dcerpc_pipe_init(void)
+{
+ struct dcerpc_pipe *p;
+
+ TALLOC_CTX *mem_ctx = talloc_init("dcerpc_tree");
+ if (mem_ctx == NULL)
+ return NULL;
+
+ p = talloc(mem_ctx, sizeof(*p));
+ if (!p) {
+ talloc_destroy(mem_ctx);
+ return NULL;
+ }
+
+ p->reference_count = 0;
+ p->mem_ctx = mem_ctx;
+ p->call_id = 1;
+ p->auth_info = NULL;
+ p->security_state = NULL;
+ p->flags = 0;
+ p->srv_max_xmit_frag = 0;
+ p->srv_max_recv_frag = 0;
+
+ return p;
+}
+
+/* close down a dcerpc over SMB pipe */
+void dcerpc_pipe_close(struct dcerpc_pipe *p)
+{
+ if (!p) return;
+ p->reference_count--;
+ if (p->reference_count <= 0) {
+ if (p->security_state) {
+ p->security_state->security_end(p->security_state);
+ }
+ p->transport.shutdown_pipe(p);
+ talloc_destroy(p->mem_ctx);
+ }
+}
+
+/* we need to be able to get/set the fragment length without doing a full
+ decode */
+void dcerpc_set_frag_length(DATA_BLOB *blob, uint16 v)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+ } else {
+ RSSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, v);
+ }
+}
+
+uint16 dcerpc_get_frag_length(const DATA_BLOB *blob)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ return SVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
+ } else {
+ return RSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET);
+ }
+}
+
+void dcerpc_set_auth_length(DATA_BLOB *blob, uint16 v)
+{
+ if (CVAL(blob->data,DCERPC_DREP_OFFSET) & DCERPC_DREP_LE) {
+ SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
+ } else {
+ RSSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, v);
+ }
+}
+
+
+/*
+ parse a data blob into a dcerpc_packet structure. This handles both
+ input and output packets
+*/
+static NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct dcerpc_packet *pkt)
+{
+ struct ndr_pull *ndr;
+
+ ndr = ndr_pull_init_blob(blob, mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ return ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+}
+
+/*
+ parse a possibly signed blob into a dcerpc request packet structure
+*/
+static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct dcerpc_packet *pkt)
+{
+ struct ndr_pull *ndr;
+ NTSTATUS status;
+ struct dcerpc_auth auth;
+ DATA_BLOB auth_blob;
+
+ /* non-signed packets are simpler */
+ if (!p->auth_info || !p->security_state) {
+ return dcerpc_pull(blob, mem_ctx, pkt);
+ }
+
+ ndr = ndr_pull_init_blob(blob, mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ /* pull the basic packet */
+ status = ndr_pull_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pkt->ptype != DCERPC_PKT_RESPONSE) {
+ return status;
+ }
+
+ auth_blob.length = 8 + pkt->auth_length;
+
+ /* check for a valid length */
+ if (pkt->u.response.stub_and_verifier.length < auth_blob.length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ auth_blob.data =
+ pkt->u.response.stub_and_verifier.data +
+ pkt->u.response.stub_and_verifier.length - auth_blob.length;
+ pkt->u.response.stub_and_verifier.length -= auth_blob.length;
+
+ /* pull the auth structure */
+ ndr = ndr_pull_init_blob(&auth_blob, mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (! (CVAL(blob->data, DCERPC_DREP_OFFSET) & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+
+ /* check signature or unseal the packet */
+ switch (p->auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = p->security_state->unseal_packet(p->security_state,
+ pkt->u.response.stub_and_verifier.data,
+ pkt->u.response.stub_and_verifier.length,
+ &auth.credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = p->security_state->check_packet(p->security_state,
+ pkt->u.response.stub_and_verifier.data,
+ pkt->u.response.stub_and_verifier.length,
+ &auth.credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ /* remove the indicated amount of paddiing */
+ if (pkt->u.response.stub_and_verifier.length < auth.auth_pad_length) {
+ return NT_STATUS_INFO_LENGTH_MISMATCH;
+ }
+ pkt->u.response.stub_and_verifier.length -= auth.auth_pad_length;
+
+ return status;
+}
+
+
+/*
+ push a dcerpc request packet into a blob, possibly signing it.
+*/
+static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
+ DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct dcerpc_packet *pkt)
+{
+ NTSTATUS status;
+ struct ndr_push *ndr;
+
+ /* non-signed packets are simpler */
+ if (!p->auth_info || !p->security_state) {
+ return dcerpc_push_auth(blob, mem_ctx, pkt, p->auth_info);
+ }
+
+ ndr = ndr_push_init_ctx(mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* pad to 8 byte multiple */
+ p->auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
+ ndr_push_zero(ndr, p->auth_info->auth_pad_length);
+
+ /* sign or seal the packet */
+ switch (p->auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = p->security_state->seal_packet(p->security_state,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ &p->auth_info->credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = p->security_state->sign_packet(p->security_state,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ &p->auth_info->credentials);
+ break;
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ p->auth_info->credentials = data_blob(NULL, 0);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* add the auth verifier */
+ status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, p->auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* extract the whole packet as a blob */
+ *blob = ndr_push_blob(ndr);
+
+ /* fill in the fragment length and auth_length, we can't fill
+ in these earlier as we don't know the signature length (it
+ could be variable length) */
+ dcerpc_set_frag_length(blob, blob->length);
+ dcerpc_set_auth_length(blob, p->auth_info->credentials.length);
+
+ data_blob_free(&p->auth_info->credentials);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ fill in the fixed values in a dcerpc header
+*/
+static void init_dcerpc_hdr(struct dcerpc_pipe *p, struct dcerpc_packet *pkt)
+{
+ pkt->rpc_vers = 5;
+ pkt->rpc_vers_minor = 0;
+ if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+ pkt->drep[0] = 0;
+ } else {
+ pkt->drep[0] = DCERPC_DREP_LE;
+ }
+ pkt->drep[1] = 0;
+ pkt->drep[2] = 0;
+ pkt->drep[3] = 0;
+}
+
+
+/*
+ perform a bind using the given syntax
+
+ the auth_info structure is updated with the reply authentication info
+ on success
+*/
+NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const struct dcerpc_syntax_id *syntax,
+ const struct dcerpc_syntax_id *transfer_syntax)
+{
+ struct dcerpc_packet pkt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+ struct dcerpc_syntax_id tsyntax;
+
+ init_dcerpc_hdr(p, &pkt);
+
+ pkt.ptype = DCERPC_PKT_BIND;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->call_id;
+ pkt.auth_length = 0;
+
+ pkt.u.bind.max_xmit_frag = 0x2000;
+ pkt.u.bind.max_recv_frag = 0x2000;
+ pkt.u.bind.assoc_group_id = 0;
+ pkt.u.bind.num_contexts = 1;
+ pkt.u.bind.ctx_list = talloc(mem_ctx, sizeof(pkt.u.bind.ctx_list[0]));
+ if (!pkt.u.bind.ctx_list) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ pkt.u.bind.ctx_list[0].context_id = 0;
+ pkt.u.bind.ctx_list[0].num_transfer_syntaxes = 1;
+ pkt.u.bind.ctx_list[0].abstract_syntax = *syntax;
+ tsyntax = *transfer_syntax;
+ pkt.u.bind.ctx_list[0].transfer_syntaxes = &tsyntax;
+ pkt.u.bind.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* send it on its way */
+ status = p->transport.full_request(p, mem_ctx, &blob, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* unmarshall the NDR */
+ status = dcerpc_pull(&blob, mem_ctx, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if ((pkt.ptype != DCERPC_PKT_BIND_ACK && pkt.ptype != DCERPC_PKT_ALTER_ACK) ||
+ pkt.u.bind_ack.num_results == 0 ||
+ pkt.u.bind_ack.ctx_list[0].result != 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (pkt.ptype != DCERPC_PKT_ALTER_ACK) {
+ p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag;
+ p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag;
+ }
+
+ /* the bind_ack might contain a reply set of credentials */
+ if (p->auth_info && pkt.u.bind_ack.auth_info.length) {
+ status = ndr_pull_struct_blob(&pkt.u.bind_ack.auth_info,
+ mem_ctx,
+ p->auth_info,
+ (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
+ }
+
+ return status;
+}
+
+/*
+ perform a continued bind (and auth3)
+*/
+NTSTATUS dcerpc_auth3(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx)
+{
+ struct dcerpc_packet pkt;
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ init_dcerpc_hdr(p, &pkt);
+
+ pkt.ptype = DCERPC_PKT_AUTH3;
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ pkt.call_id = p->call_id++;
+ pkt.auth_length = 0;
+ pkt.u.auth._pad = 0;
+ pkt.u.auth.auth_info = data_blob(NULL, 0);
+
+ /* construct the NDR form of the packet */
+ status = dcerpc_push_auth(&blob, mem_ctx, &pkt, p->auth_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* send it on its way */
+ status = p->transport.initial_request(p, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return status;
+}
+
+
+/* perform a dcerpc bind, using the uuid as the key */
+NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ const char *uuid, unsigned version)
+{
+ struct dcerpc_syntax_id syntax;
+ struct dcerpc_syntax_id transfer_syntax;
+ NTSTATUS status;
+
+ status = GUID_from_string(uuid, &syntax.uuid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2,("Invalid uuid string in dcerpc_bind_byuuid\n"));
+ return status;
+ }
+ syntax.if_version = version;
+
+ status = GUID_from_string(NDR_GUID, &transfer_syntax.uuid);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ transfer_syntax.if_version = NDR_GUID_VERSION;
+
+ return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax);
+}
+
+/*
+ perform a full request/response pair on a dcerpc pipe
+*/
+NTSTATUS dcerpc_request(struct dcerpc_pipe *p,
+ uint16 opnum,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *stub_data_in,
+ DATA_BLOB *stub_data_out)
+{
+
+ struct dcerpc_packet pkt;
+ NTSTATUS status;
+ DATA_BLOB blob, payload;
+ uint32 remaining, chunk_size;
+
+ init_dcerpc_hdr(p, &pkt);
+
+ remaining = stub_data_in->length;
+
+ /* we can write a full max_recv_frag size, minus the dcerpc
+ request header size */
+ chunk_size = p->srv_max_recv_frag - (DCERPC_MAX_SIGN_SIZE+DCERPC_REQUEST_LENGTH);
+
+ pkt.ptype = DCERPC_PKT_REQUEST;
+ pkt.call_id = p->call_id++;
+ pkt.auth_length = 0;
+ pkt.u.request.alloc_hint = remaining;
+ pkt.u.request.context_id = 0;
+ pkt.u.request.opnum = opnum;
+
+ /* we send a series of pdus without waiting for a reply until
+ the last pdu */
+ while (remaining > chunk_size) {
+ if (remaining == stub_data_in->length) {
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
+ } else {
+ pkt.pfc_flags = 0;
+ }
+
+ pkt.u.request.stub_and_verifier.data = stub_data_in->data +
+ (stub_data_in->length - remaining);
+ pkt.u.request.stub_and_verifier.length = chunk_size;
+
+ status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = p->transport.initial_request(p, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ remaining -= chunk_size;
+ }
+
+ /* now we send a pdu with LAST_FRAG sent and get the first
+ part of the reply */
+ if (remaining == stub_data_in->length) {
+ pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST;
+ } else {
+ pkt.pfc_flags = DCERPC_PFC_FLAG_LAST;
+ }
+ pkt.u.request.stub_and_verifier.data = stub_data_in->data +
+ (stub_data_in->length - remaining);
+ pkt.u.request.stub_and_verifier.length = remaining;
+
+ status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* send the pdu and get the initial response pdu */
+ status = p->transport.full_request(p, mem_ctx, &blob, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pkt.ptype == DCERPC_PKT_FAULT) {
+ p->last_fault_code = pkt.u.fault.status;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (pkt.ptype != DCERPC_PKT_RESPONSE) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!(pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST)) {
+ /* something is badly wrong! */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ payload = pkt.u.response.stub_and_verifier;
+
+ /* continue receiving fragments */
+ while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) {
+ uint32 length;
+
+ status = p->transport.secondary_request(p, mem_ctx, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pkt.pfc_flags & DCERPC_PFC_FLAG_FIRST) {
+ /* start of another packet!? */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (pkt.ptype == DCERPC_PKT_FAULT) {
+ p->last_fault_code = pkt.u.fault.status;
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ if (pkt.ptype != DCERPC_PKT_RESPONSE) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ length = pkt.u.response.stub_and_verifier.length;
+
+ payload.data = talloc_realloc(mem_ctx,
+ payload.data,
+ payload.length + length);
+ if (!payload.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(payload.data + payload.length,
+ pkt.u.response.stub_and_verifier.data,
+ length);
+
+ payload.length += length;
+ }
+
+ if (stub_data_out) {
+ *stub_data_out = payload;
+ }
+
+ if (!(pkt.drep[0] & DCERPC_DREP_LE)) {
+ p->flags |= DCERPC_PULL_BIGENDIAN;
+ } else {
+ p->flags &= ~DCERPC_PULL_BIGENDIAN;
+ }
+
+ return status;
+}
+
+
+/*
+ this is a paranoid NDR validator. For every packet we push onto the wire
+ we pull it back again, then push it again. Then we compare the raw NDR data
+ for that to the NDR we initially generated. If they don't match then we know
+ we must have a bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_in(TALLOC_CTX *mem_ctx,
+ DATA_BLOB blob,
+ size_t struct_size,
+ NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
+ NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ NTSTATUS status;
+ DATA_BLOB blob2;
+
+ st = talloc(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull = ndr_pull_init_blob(&blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_pull(pull, NDR_IN, st);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed input validation pull - %s",
+ nt_errstr(status));
+ }
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push(push, NDR_IN, st);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed input validation push - %s",
+ nt_errstr(status));
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (!data_blob_equal(&blob, &blob2)) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ return ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed input validation data - %s",
+ nt_errstr(status));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ this is a paranoid NDR input validator. For every packet we pull
+ from the wire we push it back again then pull and push it
+ again. Then we compare the raw NDR data for that to the NDR we
+ initially generated. If they don't match then we know we must have a
+ bug in either the pull or push side of our code
+*/
+static NTSTATUS dcerpc_ndr_validate_out(TALLOC_CTX *mem_ctx,
+ void *struct_ptr,
+ size_t struct_size,
+ NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
+ NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *))
+{
+ void *st;
+ struct ndr_pull *pull;
+ struct ndr_push *push;
+ NTSTATUS status;
+ DATA_BLOB blob, blob2;
+
+ st = talloc(mem_ctx, struct_size);
+ if (!st) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(st, struct_ptr, struct_size);
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push(push, NDR_OUT, struct_ptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push - %s",
+ nt_errstr(status));
+ }
+
+ blob = ndr_push_blob(push);
+
+ pull = ndr_pull_init_blob(&blob, mem_ctx);
+ if (!pull) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pull->flags |= LIBNDR_FLAG_REF_ALLOC;
+ status = ndr_pull(pull, NDR_OUT, st);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ndr_pull_error(pull, NDR_ERR_VALIDATE,
+ "failed output validation pull - %s",
+ nt_errstr(status));
+ }
+
+ push = ndr_push_init_ctx(mem_ctx);
+ if (!push) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ndr_push(push, NDR_OUT, st);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation push2 - %s",
+ nt_errstr(status));
+ }
+
+ blob2 = ndr_push_blob(push);
+
+ if (!data_blob_equal(&blob, &blob2)) {
+ DEBUG(3,("original:\n"));
+ dump_data(3, blob.data, blob.length);
+ DEBUG(3,("secondary:\n"));
+ dump_data(3, blob2.data, blob2.length);
+ return ndr_push_error(push, NDR_ERR_VALIDATE,
+ "failed output validation data - %s",
+ nt_errstr(status));
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ a useful helper function for synchronous rpc requests
+
+ this can be used when you have ndr push/pull functions in the
+ standard format
+*/
+NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p,
+ uint32 opnum,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS (*ndr_push)(struct ndr_push *, int, void *),
+ NTSTATUS (*ndr_pull)(struct ndr_pull *, int, void *),
+ void *struct_ptr,
+ size_t struct_size)
+{
+ struct ndr_push *push;
+ struct ndr_pull *pull;
+ NTSTATUS status;
+ DATA_BLOB request, response;
+
+ /* setup for a ndr_push_* call */
+ push = ndr_push_init();
+ if (!push) {
+ talloc_destroy(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (p->flags & DCERPC_PUSH_BIGENDIAN) {
+ push->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ /* push the structure into a blob */
+ status = ndr_push(push, NDR_IN, struct_ptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* retrieve the blob */
+ request = ndr_push_blob(push);
+
+ if (p->flags & DCERPC_DEBUG_VALIDATE_IN) {
+ status = dcerpc_ndr_validate_in(mem_ctx, request, struct_size,
+ ndr_push, ndr_pull);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ DEBUG(10,("rpc request data:\n"));
+ dump_data(10, request.data, request.length);
+
+ /* make the actual dcerpc request */
+ status = dcerpc_request(p, opnum, mem_ctx, &request, &response);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* prepare for ndr_pull_* */
+ pull = ndr_pull_init_blob(&response, mem_ctx);
+ if (!pull) {
+ goto failed;
+ }
+
+ if (p->flags & DCERPC_PULL_BIGENDIAN) {
+ pull->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ DEBUG(10,("rpc reply data:\n"));
+ dump_data(10, pull->data, pull->data_size);
+
+ /* pull the structure from the blob */
+ status = ndr_pull(pull, NDR_OUT, struct_ptr);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* possibly check the packet signature */
+
+
+ if (p->flags & DCERPC_DEBUG_VALIDATE_OUT) {
+ status = dcerpc_ndr_validate_out(mem_ctx, struct_ptr, struct_size,
+ ndr_push, ndr_pull);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ if (pull->offset != pull->data_size) {
+ DEBUG(0,("Warning! %d unread bytes\n", pull->data_size - pull->offset));
+ status = NT_STATUS_INFO_LENGTH_MISMATCH;
+ goto failed;
+ }
+
+failed:
+ ndr_push_free(push);
+ return status;
+}
+
+
+/*
+ a useful function for retrieving the server name we connected to
+*/
+const char *dcerpc_server_name(struct dcerpc_pipe *p)
+{
+ if (!p->transport.peer_name) {
+ return "";
+ }
+ return p->transport.peer_name(p);
+}
diff --git a/source/librpc/rpc/dcerpc.h b/source/librpc/rpc/dcerpc.h
new file mode 100644
index 00000000000..55c81c374e5
--- /dev/null
+++ b/source/librpc/rpc/dcerpc.h
@@ -0,0 +1,117 @@
+/*
+ Unix SMB/CIFS implementation.
+ DCERPC interface structures
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003
+
+ 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.
+*/
+
+enum dcerpc_transport_t {NCACN_NP, NCACN_IP_TCP};
+
+/*
+ this defines a generic security context for signed/sealed dcerpc pipes.
+*/
+struct dcerpc_security {
+ void *private;
+ NTSTATUS (*unseal_packet)(struct dcerpc_security *,
+ uchar *data, size_t length, DATA_BLOB *sig);
+ NTSTATUS (*check_packet)(struct dcerpc_security *,
+ const uchar *data, size_t length, const DATA_BLOB *sig);
+ NTSTATUS (*seal_packet)(struct dcerpc_security *,
+ uchar *data, size_t length, DATA_BLOB *sig);
+ NTSTATUS (*sign_packet)(struct dcerpc_security *,
+ const uchar *data, size_t length, DATA_BLOB *sig);
+ void (*security_end)(struct dcerpc_security *);
+};
+
+
+struct dcerpc_pipe {
+ TALLOC_CTX *mem_ctx;
+ int reference_count;
+ uint32 call_id;
+ uint32 srv_max_xmit_frag;
+ uint32 srv_max_recv_frag;
+ unsigned flags;
+ struct dcerpc_security *security_state;
+ struct dcerpc_auth *auth_info;
+ const char *binding_string;
+
+ struct dcerpc_transport {
+ enum dcerpc_transport_t transport;
+ void *private;
+ NTSTATUS (*full_request)(struct dcerpc_pipe *,
+ TALLOC_CTX *, DATA_BLOB *, DATA_BLOB *);
+ NTSTATUS (*secondary_request)(struct dcerpc_pipe *, TALLOC_CTX *, DATA_BLOB *);
+ NTSTATUS (*initial_request)(struct dcerpc_pipe *, TALLOC_CTX *, DATA_BLOB *);
+ NTSTATUS (*shutdown_pipe)(struct dcerpc_pipe *);
+ const char *(*peer_name)(struct dcerpc_pipe *);
+ } transport;
+
+ /* the last fault code from a DCERPC fault */
+ uint32 last_fault_code;
+};
+
+/* dcerpc pipe flags */
+#define DCERPC_DEBUG_PRINT_IN (1<<0)
+#define DCERPC_DEBUG_PRINT_OUT (1<<1)
+#define DCERPC_DEBUG_PRINT_BOTH (DCERPC_DEBUG_PRINT_IN | DCERPC_DEBUG_PRINT_OUT)
+
+#define DCERPC_DEBUG_VALIDATE_IN 4
+#define DCERPC_DEBUG_VALIDATE_OUT 8
+#define DCERPC_DEBUG_VALIDATE_BOTH (DCERPC_DEBUG_VALIDATE_IN | DCERPC_DEBUG_VALIDATE_OUT)
+
+#define DCERPC_SIGN 16
+#define DCERPC_SEAL 32
+
+#define DCERPC_PUSH_BIGENDIAN 64
+#define DCERPC_PULL_BIGENDIAN 128
+
+#define DCERPC_SCHANNEL 256
+
+/*
+ this is used to find pointers to calls
+*/
+struct dcerpc_interface_call {
+ const char *name;
+ size_t struct_size;
+ NTSTATUS (*ndr_push)(struct ndr_push *, int , void *);
+ NTSTATUS (*ndr_pull)(struct ndr_pull *, int , void *);
+ void (*ndr_print)(struct ndr_print *, const char *, int, void *);
+};
+
+struct dcerpc_endpoint_list {
+ uint32 count;
+ const char * const *names;
+};
+
+struct dcerpc_interface_table {
+ const char *name;
+ const char *uuid;
+ uint32 if_version;
+ uint32 num_calls;
+ const struct dcerpc_interface_call *calls;
+ const struct dcerpc_endpoint_list *endpoints;
+};
+
+
+/* this describes a binding to a particular transport/pipe */
+struct dcerpc_binding {
+ enum dcerpc_transport_t transport;
+ const char *host;
+ const char **options;
+ uint32 flags;
+};
diff --git a/source/librpc/rpc/dcerpc_auth.c b/source/librpc/rpc/dcerpc_auth.c
new file mode 100644
index 00000000000..2b01ad2d4e9
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_auth.c
@@ -0,0 +1,43 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc authentication operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/*
+ do a non-athenticated dcerpc bind
+*/
+NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
+ const char *uuid, unsigned version)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+
+ mem_ctx = talloc_init("dcerpc_bind_auth_ntlm");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
+ talloc_destroy(mem_ctx);
+
+ return status;
+}
diff --git a/source/librpc/rpc/dcerpc_lsa.c b/source/librpc/rpc/dcerpc_lsa.c
new file mode 100644
index 00000000000..482853c0abd
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_lsa.c
@@ -0,0 +1,79 @@
+/*
+ Unix SMB/CIFS implementation.
+ raw dcerpc operations
+
+ Copyright (C) Tim Potter 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
+ 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"
+
+NTSTATUS lsa_OpenPolicy(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy r;
+ NTSTATUS status;
+ uint16 system_name = '\\';
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = &system_name;
+ r.in.attr = &attr;
+ r.in.desired_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ r.out.handle = handle;
+
+ return dcerpc_lsa_OpenPolicy(p, mem_ctx, &r);
+}
+
+NTSTATUS lsa_OpenPolicy2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx,
+ struct policy_handle *handle)
+{
+ struct lsa_ObjectAttribute attr;
+ struct lsa_QosInfo qos;
+ struct lsa_OpenPolicy2 r;
+
+ qos.len = 0;
+ qos.impersonation_level = 2;
+ qos.context_mode = 1;
+ qos.effective_only = 0;
+
+ attr.len = 0;
+ attr.root_dir = NULL;
+ attr.object_name = NULL;
+ attr.attributes = 0;
+ attr.sec_desc = NULL;
+ attr.sec_qos = &qos;
+
+ r.in.system_name = "\\";
+ r.in.attr = &attr;
+ r.in.desired_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
+ r.out.handle = handle;
+
+ return dcerpc_lsa_OpenPolicy2(p, mem_ctx, &r);
+}
+
diff --git a/source/librpc/rpc/dcerpc_ntlm.c b/source/librpc/rpc/dcerpc_ntlm.c
new file mode 100644
index 00000000000..81f434cccfd
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_ntlm.c
@@ -0,0 +1,197 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc authentication operations
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/*
+ wrappers for the ntlmssp_*() functions
+*/
+static NTSTATUS ntlm_unseal_packet(struct dcerpc_security *dcerpc_security,
+ uchar *data, size_t length, DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+ return ntlmssp_unseal_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_check_packet(struct dcerpc_security *dcerpc_security,
+ const uchar *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+ return ntlmssp_check_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_seal_packet(struct dcerpc_security *dcerpc_security,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+ return ntlmssp_seal_packet(ntlmssp_state, data, length, sig);
+}
+
+static NTSTATUS ntlm_sign_packet(struct dcerpc_security *dcerpc_security,
+ const uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+ return ntlmssp_sign_packet(ntlmssp_state, data, length, sig);
+}
+
+static void ntlm_security_end(struct dcerpc_security *dcerpc_security)
+{
+ struct ntlmssp_state *ntlmssp_state = dcerpc_security->private;
+ ntlmssp_end(&ntlmssp_state);
+}
+
+
+
+/*
+ do ntlm style authentication on a dcerpc pipe
+*/
+NTSTATUS dcerpc_bind_auth_ntlm(struct dcerpc_pipe *p,
+ const char *uuid, unsigned version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ NTSTATUS status;
+ struct ntlmssp_state *state;
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB credentials;
+
+ mem_ctx = talloc_init("dcerpc_bind_auth_ntlm");
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntlmssp_client_start(&state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = ntlmssp_set_domain(state, domain);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = ntlmssp_set_username(state, username);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = ntlmssp_set_password(state, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
+ if (!p->auth_info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p->auth_info->auth_type = DCERPC_AUTH_TYPE_NTLMSSP;
+
+ if (p->flags & DCERPC_SEAL) {
+ p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN | NTLMSSP_NEGOTIATE_SEAL;
+ } else {
+ /* ntlmssp does not work on dcerpc with
+ AUTH_LEVEL_NONE */
+ state->neg_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ }
+ p->auth_info->auth_pad_length = 0;
+ p->auth_info->auth_reserved = 0;
+ p->auth_info->auth_context_id = random();
+ p->auth_info->credentials = data_blob(NULL, 0);
+ p->security_state = NULL;
+
+ status = ntlmssp_update(state,
+ p->auth_info->credentials,
+ &credentials);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ }
+
+ p->auth_info->credentials = data_blob_talloc(mem_ctx,
+ credentials.data,
+ credentials.length);
+ data_blob_free(&credentials);
+
+ status = dcerpc_bind_byuuid(p, mem_ctx, uuid, version);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+
+ status = ntlmssp_update(state,
+ p->auth_info->credentials,
+ &credentials);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ goto done;
+ }
+
+ p->auth_info->credentials = data_blob_talloc(mem_ctx,
+ credentials.data,
+ credentials.length);
+ data_blob_free(&credentials);
+
+ status = dcerpc_auth3(p, mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
+ if (!p->security_state) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p->security_state->private = state;
+ p->security_state->unseal_packet = ntlm_unseal_packet;
+ p->security_state->check_packet = ntlm_check_packet;
+ p->security_state->seal_packet = ntlm_seal_packet;
+ p->security_state->sign_packet = ntlm_sign_packet;
+ p->security_state->security_end = ntlm_security_end;
+
+ switch (p->auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ /* setup for signing */
+ status = ntlmssp_sign_init(state);
+ break;
+ }
+
+done:
+ talloc_destroy(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ p->security_state = NULL;
+ p->auth_info = NULL;
+ }
+
+ return status;
+}
+
+
diff --git a/source/librpc/rpc/dcerpc_schannel.c b/source/librpc/rpc/dcerpc_schannel.c
new file mode 100644
index 00000000000..2a4c0a6bb1b
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_schannel.c
@@ -0,0 +1,266 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc schannel operations
+
+ Copyright (C) Andrew Tridgell 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
+ 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"
+
+/*
+ wrappers for the schannel_*() functions
+*/
+static NTSTATUS schan_unseal_packet(struct dcerpc_security *dcerpc_security,
+ uchar *data, size_t length, DATA_BLOB *sig)
+{
+ struct schannel_state *schannel_state = dcerpc_security->private;
+ return schannel_unseal_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_check_packet(struct dcerpc_security *dcerpc_security,
+ const uchar *data, size_t length,
+ const DATA_BLOB *sig)
+{
+ struct schannel_state *schannel_state = dcerpc_security->private;
+ return schannel_check_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_seal_packet(struct dcerpc_security *dcerpc_security,
+ uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct schannel_state *schannel_state = dcerpc_security->private;
+ return schannel_seal_packet(schannel_state, data, length, sig);
+}
+
+static NTSTATUS schan_sign_packet(struct dcerpc_security *dcerpc_security,
+ const uchar *data, size_t length,
+ DATA_BLOB *sig)
+{
+ struct schannel_state *schannel_state = dcerpc_security->private;
+ return schannel_sign_packet(schannel_state, data, length, sig);
+}
+
+static void schan_security_end(struct dcerpc_security *dcerpc_security)
+{
+ struct schannel_state *schannel_state = dcerpc_security->private;
+ schannel_end(&schannel_state);
+}
+
+
+/*
+ get a schannel key using a netlogon challenge on a secondary pipe
+*/
+NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
+ const char *domain,
+ const char *username,
+ const char *password,
+ int chan_type,
+ uint8 new_session_key[8])
+{
+ NTSTATUS status;
+ struct dcerpc_pipe *p2;
+ struct netr_ServerReqChallenge r;
+ struct netr_ServerAuthenticate2 a;
+ uint8 mach_pwd[16];
+ struct netr_CredentialState creds;
+ const char *workgroup, *workstation;
+ uint32 negotiate_flags = 0;
+
+ workstation = username;
+ workgroup = domain;
+
+ /*
+ step 1 - establish a netlogon connection, with no authentication
+ */
+ status = dcerpc_secondary_smb(p, &p2,
+ DCERPC_NETLOGON_NAME,
+ DCERPC_NETLOGON_UUID,
+ DCERPC_NETLOGON_VERSION);
+
+
+ /*
+ step 2 - request a netlogon challenge
+ */
+ r.in.server_name = talloc_asprintf(p->mem_ctx, "\\\\%s", dcerpc_server_name(p));
+ r.in.computer_name = workstation;
+ generate_random_buffer(r.in.credentials.data, sizeof(r.in.credentials.data), False);
+
+ status = dcerpc_netr_ServerReqChallenge(p2, p->mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /*
+ step 3 - authenticate on the netlogon pipe
+ */
+ E_md4hash(password, mach_pwd);
+ creds_client_init(&creds, &r.in.credentials, &r.out.credentials, mach_pwd,
+ &a.in.credentials);
+
+ a.in.server_name = r.in.server_name;
+ a.in.username = talloc_asprintf(p->mem_ctx, "%s$", workstation);
+ a.in.secure_channel_type = chan_type;
+ a.in.computer_name = workstation;
+ a.in.negotiate_flags = &negotiate_flags;
+ a.out.negotiate_flags = &negotiate_flags;
+
+ status = dcerpc_netr_ServerAuthenticate2(p2, p->mem_ctx, &a);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!creds_client_check(&creds, &a.out.credentials)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /*
+ the schannel session key is now in creds.session_key
+
+ we no longer need the netlogon pipe open
+ */
+ dcerpc_pipe_close(p2);
+
+ memcpy(new_session_key, creds.session_key, 8);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ do a schannel style bind on a dcerpc pipe with the given schannel
+ key. The username is usually of the form HOSTNAME$ and the password
+ is the domain trust password
+*/
+NTSTATUS dcerpc_bind_auth_schannel_key(struct dcerpc_pipe *p,
+ const char *uuid, unsigned version,
+ const char *domain,
+ const char *username,
+ const uint8 session_key[8])
+{
+ NTSTATUS status;
+ uint8 full_session_key[16];
+ struct schannel_state *schannel_state;
+ const char *workgroup, *workstation;
+
+ memcpy(full_session_key, session_key, 8);
+ memset(full_session_key+8, 0, 8);
+
+ workstation = username;
+ workgroup = domain;
+
+ /*
+ perform a bind with security type schannel
+ */
+ p->auth_info = talloc(p->mem_ctx, sizeof(*p->auth_info));
+ if (!p->auth_info) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ p->auth_info->auth_type = DCERPC_AUTH_TYPE_SCHANNEL;
+
+ if (p->flags & DCERPC_SEAL) {
+ p->auth_info->auth_level = DCERPC_AUTH_LEVEL_PRIVACY;
+ } else {
+ /* note that DCERPC_AUTH_LEVEL_NONE does not make any
+ sense, and would be rejected by the server */
+ p->auth_info->auth_level = DCERPC_AUTH_LEVEL_INTEGRITY;
+ }
+ p->auth_info->auth_pad_length = 0;
+ p->auth_info->auth_reserved = 0;
+ p->auth_info->auth_context_id = random();
+ p->security_state = NULL;
+
+ p->auth_info->credentials = data_blob_talloc(p->mem_ctx,
+ NULL,
+ 8 +
+ strlen(workgroup)+1 +
+ strlen(workstation)+1);
+ if (!p->auth_info->credentials.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* oh, this is ugly! */
+ SIVAL(p->auth_info->credentials.data, 0, 0);
+ SIVAL(p->auth_info->credentials.data, 4, 3);
+ memcpy(p->auth_info->credentials.data+8, workgroup, strlen(workgroup)+1);
+ memcpy(p->auth_info->credentials.data+8+strlen(workgroup)+1,
+ workstation, strlen(workstation)+1);
+
+ /* send the authenticated bind request */
+ status = dcerpc_bind_byuuid(p, p->mem_ctx, uuid, version);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ p->security_state = talloc_p(p->mem_ctx, struct dcerpc_security);
+ if (!p->security_state) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ schannel_state = talloc_p(p->mem_ctx, struct schannel_state);
+ if (!schannel_state) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ status = schannel_start(&schannel_state, full_session_key, True);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ dump_data_pw("session key:\n", schannel_state->session_key, 16);
+
+ p->security_state->private = schannel_state;
+ p->security_state->unseal_packet = schan_unseal_packet;
+ p->security_state->check_packet = schan_check_packet;
+ p->security_state->seal_packet = schan_seal_packet;
+ p->security_state->sign_packet = schan_sign_packet;
+ p->security_state->security_end = schan_security_end;
+
+done:
+ return status;
+}
+
+
+/*
+ do a schannel style bind on a dcerpc pipe. The username is usually
+ of the form HOSTNAME$ and the password is the domain trust password
+*/
+NTSTATUS dcerpc_bind_auth_schannel(struct dcerpc_pipe *p,
+ const char *uuid, unsigned version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ NTSTATUS status;
+ uint8 session_key[8];
+
+ status = dcerpc_schannel_key(p, domain, username, password,
+ lp_server_role() == ROLE_DOMAIN_BDC? SEC_CHAN_BDC:SEC_CHAN_WKSTA,
+ session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = dcerpc_bind_auth_schannel_key(p, uuid, version, domain, username, session_key);
+
+ return status;
+}
+
diff --git a/source/librpc/rpc/dcerpc_smb.c b/source/librpc/rpc/dcerpc_smb.c
new file mode 100644
index 00000000000..7822231b826
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_smb.c
@@ -0,0 +1,391 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over SMB transport
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/* transport private information used by SMB pipe transport */
+struct smb_private {
+ uint16 fnum;
+ struct cli_tree *tree;
+};
+
+static struct cli_request *dcerpc_raw_send(struct dcerpc_pipe *p, DATA_BLOB *blob)
+{
+ struct smb_private *smb = p->transport.private;
+ struct smb_trans2 trans;
+ uint16 setup[2];
+ struct cli_request *req;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("dcerpc_raw_send");
+ if (!mem_ctx) return NULL;
+
+ trans.in.data = *blob;
+ trans.in.params = data_blob(NULL, 0);
+
+ setup[0] = TRANSACT_DCERPCCMD;
+ setup[1] = smb->fnum;
+
+ trans.in.max_param = 0;
+ trans.in.max_data = 0x8000;
+ trans.in.max_setup = 0;
+ trans.in.setup_count = 2;
+ trans.in.flags = 0;
+ trans.in.timeout = 0;
+ trans.in.setup = setup;
+ trans.in.trans_name = "\\PIPE\\";
+
+ req = smb_raw_trans_send(smb->tree, &trans);
+
+ talloc_destroy(mem_ctx);
+
+ return req;
+}
+
+
+static NTSTATUS dcerpc_raw_recv(struct dcerpc_pipe *p,
+ struct cli_request *req,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_private *smb = p->transport.private;
+ struct smb_trans2 trans;
+ NTSTATUS status;
+ uint16 frag_length;
+ DATA_BLOB payload;
+
+ status = smb_raw_trans_recv(req, mem_ctx, &trans);
+ /* STATUS_BUFFER_OVERFLOW means that there is more data
+ available via SMBreadX */
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ return status;
+ }
+
+ payload = trans.out.data;
+
+ if (trans.out.data.length < 16 ||
+ !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ goto done;
+ }
+
+ /* we might have recieved a partial fragment, in which case we
+ need to pull the rest of it */
+ frag_length = dcerpc_get_frag_length(&payload);
+ if (frag_length <= payload.length) {
+ goto done;
+ }
+
+ /* make sure the payload can hold the whole fragment */
+ payload.data = talloc_realloc(mem_ctx, payload.data, frag_length);
+ if (!payload.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* the rest of the data is available via SMBreadX */
+ while (frag_length > payload.length) {
+ uint32 n;
+ union smb_read io;
+
+ n = frag_length - payload.length;
+ if (n > 0xFF00) {
+ n = 0xFF00;
+ }
+
+ io.generic.level = RAW_READ_READX;
+ io.readx.in.fnum = smb->fnum;
+ io.readx.in.mincnt = n;
+ io.readx.in.maxcnt = n;
+ io.readx.in.offset = 0;
+ io.readx.in.remaining = 0;
+ io.readx.out.data = payload.data + payload.length;
+ status = smb_raw_read(smb->tree, &io);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ break;
+ }
+
+ n = io.readx.out.nread;
+ if (n == 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ payload.length += n;
+
+ /* if the SMBreadX returns NT_STATUS_OK then there
+ isn't any more data to be read */
+ if (NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ }
+
+done:
+ if (blob) {
+ *blob = payload;
+ }
+
+ return status;
+}
+
+static NTSTATUS smb_full_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *request_blob,
+ DATA_BLOB *reply_blob)
+{
+ struct cli_request *req;
+ req = dcerpc_raw_send(p, request_blob);
+ return dcerpc_raw_recv(p, req, mem_ctx, reply_blob);
+}
+
+
+/*
+ retrieve a secondary pdu from a pipe
+*/
+static NTSTATUS smb_secondary_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_private *smb = p->transport.private;
+ union smb_read io;
+ uint32 n = 0x2000;
+ uint32 frag_length;
+ NTSTATUS status;
+
+ *blob = data_blob_talloc(mem_ctx, NULL, n);
+ if (!blob->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io.generic.level = RAW_READ_READX;
+ io.readx.in.fnum = smb->fnum;
+ io.readx.in.mincnt = n;
+ io.readx.in.maxcnt = n;
+ io.readx.in.offset = 0;
+ io.readx.in.remaining = 0;
+ io.readx.out.data = blob->data;
+
+ status = smb_raw_read(smb->tree, &io);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ return status;
+ }
+
+ blob->length = io.readx.out.nread;
+
+ if (blob->length < 16) {
+ return status;
+ }
+
+ frag_length = dcerpc_get_frag_length(blob);
+ if (frag_length <= blob->length) {
+ return status;
+ }
+
+ blob->data = talloc_realloc(mem_ctx, blob->data, frag_length);
+ if (!blob->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while (frag_length > blob->length &&
+ NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+
+ n = frag_length - blob->length;
+ if (n > 0xFF00) {
+ n = 0xFF00;
+ }
+
+ io.readx.in.mincnt = n;
+ io.readx.in.maxcnt = n;
+ io.readx.out.data = blob->data + blob->length;
+ status = smb_raw_read(smb->tree, &io);
+
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, STATUS_BUFFER_OVERFLOW)) {
+ return status;
+ }
+
+ n = io.readx.out.nread;
+ blob->length += n;
+ }
+
+ return status;
+}
+
+
+/*
+ send an initial pdu in a multi-pdu sequence
+*/
+static NTSTATUS smb_initial_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct smb_private *smb = p->transport.private;
+ union smb_write io;
+ NTSTATUS status;
+
+ io.generic.level = RAW_WRITE_WRITEX;
+ io.writex.in.fnum = smb->fnum;
+ io.writex.in.offset = 0;
+ io.writex.in.wmode = PIPE_START_MESSAGE;
+ io.writex.in.remaining = blob->length;
+ io.writex.in.count = blob->length;
+ io.writex.in.data = blob->data;
+
+ status = smb_raw_write(smb->tree, &io);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* make sure it accepted it all */
+ if (io.writex.out.nwritten != blob->length) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+/*
+ shutdown SMB pipe connection
+*/
+static NTSTATUS smb_shutdown_pipe(struct dcerpc_pipe *p)
+{
+ struct smb_private *smb = p->transport.private;
+ union smb_close c;
+
+ /* maybe we're still starting up */
+ if (!smb) return NT_STATUS_OK;
+
+ c.close.level = RAW_CLOSE_CLOSE;
+ c.close.in.fnum = smb->fnum;
+ c.close.in.write_time = 0;
+ smb_raw_close(smb->tree, &c);
+ cli_tree_close(smb->tree);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return SMB server name
+*/
+static const char *smb_peer_name(struct dcerpc_pipe *p)
+{
+ struct smb_private *smb = p->transport.private;
+ return smb->tree->session->transport->called.name;
+}
+
+/*
+ open a rpc connection to a named pipe
+*/
+NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe **p,
+ struct cli_tree *tree,
+ const char *pipe_name)
+{
+ struct smb_private *smb;
+ NTSTATUS status;
+ char *name = NULL;
+ union smb_open io;
+ TALLOC_CTX *mem_ctx;
+
+ asprintf(&name, "\\%s", pipe_name);
+ if (!name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ io.ntcreatex.level = RAW_OPEN_NTCREATEX;
+ io.ntcreatex.in.flags = 0;
+ io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.access_mask =
+ STD_RIGHT_READ_CONTROL_ACCESS |
+ SA_RIGHT_FILE_WRITE_ATTRIBUTES |
+ SA_RIGHT_FILE_WRITE_EA |
+ GENERIC_RIGHTS_FILE_READ |
+ GENERIC_RIGHTS_FILE_WRITE;
+ io.ntcreatex.in.file_attr = 0;
+ io.ntcreatex.in.alloc_size = 0;
+ io.ntcreatex.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.create_options = 0;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_IMPERSONATION;
+ io.ntcreatex.in.security_flags = 0;
+ io.ntcreatex.in.fname = name;
+
+ mem_ctx = talloc_init("torture_rpc_connection");
+ if (!mem_ctx) {
+ free(name);
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = smb_raw_open(tree, mem_ctx, &io);
+ free(name);
+ talloc_destroy(mem_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!(*p = dcerpc_pipe_init())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ fill in the transport methods
+ */
+ (*p)->transport.transport = NCACN_NP;
+ (*p)->transport.private = NULL;
+ (*p)->transport.full_request = smb_full_request;
+ (*p)->transport.secondary_request = smb_secondary_request;
+ (*p)->transport.initial_request = smb_initial_request;
+ (*p)->transport.shutdown_pipe = smb_shutdown_pipe;
+ (*p)->transport.peer_name = smb_peer_name;
+
+ smb = talloc((*p)->mem_ctx, sizeof(*smb));
+ if (!smb) {
+ dcerpc_pipe_close(*p);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ smb->fnum = io.ntcreatex.out.fnum;
+ smb->tree = tree;
+
+ (*p)->transport.private = smb;
+ tree->reference_count++;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the SMB tree used for a dcerpc over SMB pipe
+*/
+struct cli_tree *dcerpc_smb_tree(struct dcerpc_pipe *p)
+{
+ struct smb_private *smb = p->transport.private;
+
+ if (p->transport.transport != NCACN_NP) {
+ return NULL;
+ }
+
+ return smb->tree;
+}
diff --git a/source/librpc/rpc/dcerpc_tcp.c b/source/librpc/rpc/dcerpc_tcp.c
new file mode 100644
index 00000000000..77b536b10cb
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_tcp.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc over TCP transport
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/* transport private information used by TCP pipe transport */
+struct tcp_private {
+ int fd;
+ char *server_name;
+ uint32 port;
+};
+
+static NTSTATUS tcp_raw_recv(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct tcp_private *tcp = p->transport.private;
+ ssize_t ret;
+ uint32 frag_length;
+ DATA_BLOB blob1;
+
+ blob1 = data_blob_talloc(mem_ctx, NULL, 16);
+ if (!blob1.data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ret = read_data(tcp->fd, blob1.data, blob1.length);
+ if (ret != blob1.length) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ /* this could be a ncacn_http endpoint - this doesn't work
+ yet, but it goes close */
+ if (strncmp(blob1.data, "ncacn_http/1.0", 14) == 0) {
+ memmove(blob1.data, blob1.data+14, 2);
+ ret = read_data(tcp->fd, blob1.data+2, 14);
+ if (ret != 14) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+ }
+
+ /* we might have recieved a partial fragment, in which case we
+ need to pull the rest of it */
+ frag_length = dcerpc_get_frag_length(&blob1);
+ if (frag_length == blob1.length) {
+ *blob = blob1;
+ return NT_STATUS_OK;
+ }
+
+ *blob = data_blob_talloc(mem_ctx, NULL, frag_length);
+ if (!blob->data) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(blob->data, blob1.data, blob1.length);
+
+ ret = read_data(tcp->fd, blob->data + blob1.length, frag_length - blob1.length);
+ if (ret != frag_length - blob1.length) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tcp_full_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *request_blob,
+ DATA_BLOB *reply_blob)
+{
+ struct tcp_private *tcp = p->transport.private;
+ ssize_t ret;
+
+ ret = write_data(tcp->fd, request_blob->data, request_blob->length);
+ if (ret != request_blob->length) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return tcp_raw_recv(p, mem_ctx, reply_blob);
+}
+
+
+/*
+ retrieve a secondary pdu from a pipe
+*/
+static NTSTATUS tcp_secondary_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ return tcp_raw_recv(p, mem_ctx, blob);
+}
+
+
+/*
+ send an initial pdu in a multi-pdu sequence
+*/
+static NTSTATUS tcp_initial_request(struct dcerpc_pipe *p,
+ TALLOC_CTX *mem_ctx,
+ DATA_BLOB *blob)
+{
+ struct tcp_private *tcp = p->transport.private;
+ ssize_t ret;
+
+ ret = write_data(tcp->fd, blob->data, blob->length);
+ if (ret != blob->length) {
+ return NT_STATUS_NET_WRITE_FAULT;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ shutdown TCP pipe connection
+*/
+static NTSTATUS tcp_shutdown_pipe(struct dcerpc_pipe *p)
+{
+ struct tcp_private *tcp = p->transport.private;
+
+ if (tcp) {
+ close(tcp->fd);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return TCP server name
+*/
+static const char *tcp_peer_name(struct dcerpc_pipe *p)
+{
+ struct tcp_private *tcp = p->transport.private;
+ return tcp->server_name;
+}
+
+
+/*
+ open a rpc connection to a named pipe
+*/
+NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p,
+ const char *server,
+ uint32 port)
+{
+ struct tcp_private *tcp;
+ int fd;
+ struct in_addr addr;
+
+ if (port == 0) {
+ port = EPMAPPER_PORT;
+ }
+
+ addr.s_addr = interpret_addr(server);
+ if (addr.s_addr == 0) {
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ fd = open_socket_out(SOCK_STREAM, &addr, port, 30000);
+ if (fd == -1) {
+ return NT_STATUS_PORT_CONNECTION_REFUSED;
+ }
+
+ if (!(*p = dcerpc_pipe_init())) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ fill in the transport methods
+ */
+ (*p)->transport.transport = NCACN_IP_TCP;
+ (*p)->transport.private = NULL;
+ (*p)->transport.full_request = tcp_full_request;
+ (*p)->transport.secondary_request = tcp_secondary_request;
+ (*p)->transport.initial_request = tcp_initial_request;
+ (*p)->transport.shutdown_pipe = tcp_shutdown_pipe;
+ (*p)->transport.peer_name = tcp_peer_name;
+
+ tcp = talloc((*p)->mem_ctx, sizeof(*tcp));
+ if (!tcp) {
+ dcerpc_pipe_close(*p);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tcp->fd = fd;
+ tcp->server_name = talloc_strdup((*p)->mem_ctx, server);
+
+ (*p)->transport.private = tcp;
+
+ return NT_STATUS_OK;
+}
diff --git a/source/librpc/rpc/dcerpc_util.c b/source/librpc/rpc/dcerpc_util.c
new file mode 100644
index 00000000000..ba61f28c95d
--- /dev/null
+++ b/source/librpc/rpc/dcerpc_util.c
@@ -0,0 +1,668 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ dcerpc utility functions
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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"
+
+/*
+ this ndr_size_* stuff should really be auto-generated ....
+*/
+
+static size_t ndr_size_epm_floor(struct epm_floor *fl)
+{
+ size_t ret = 5;
+ if (fl->lhs.protocol == EPM_PROTOCOL_UUID) {
+ ret += 18;
+ } else {
+ ret += fl->lhs.info.lhs_data.length;
+ }
+ ret += fl->rhs.rhs_data.length;
+ return ret;
+}
+
+size_t ndr_size_epm_towers(struct epm_towers *towers)
+{
+ size_t ret = 2;
+ int i;
+ for (i=0;i<towers->num_floors;i++) {
+ ret += ndr_size_epm_floor(&towers->floors[i]);
+ }
+ return ret;
+}
+
+/*
+ work out what TCP port to use for a given interface on a given host
+*/
+NTSTATUS dcerpc_epm_map_tcp_port(const char *server,
+ const char *uuid, unsigned version,
+ uint32 *port)
+{
+ struct dcerpc_pipe *p;
+ NTSTATUS status;
+ struct epm_Map r;
+ struct policy_handle handle;
+ struct GUID guid;
+ struct epm_twr_t twr, *twr_r;
+
+ if (strcasecmp(uuid, DCERPC_EPMAPPER_UUID) == 0 ||
+ strcasecmp(uuid, DCERPC_MGMT_UUID) == 0) {
+ /* don't lookup epmapper via epmapper! */
+ *port = EPMAPPER_PORT;
+ return NT_STATUS_OK;
+ }
+
+ status = dcerpc_pipe_open_tcp(&p, server, EPMAPPER_PORT);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we can use the pipes memory context here as we will have a short
+ lived connection */
+ status = dcerpc_bind_byuuid(p, p->mem_ctx,
+ DCERPC_EPMAPPER_UUID,
+ DCERPC_EPMAPPER_VERSION);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_pipe_close(p);
+ return status;
+ }
+
+ ZERO_STRUCT(handle);
+ ZERO_STRUCT(guid);
+
+ twr.towers.num_floors = 5;
+ twr.towers.floors = talloc(p->mem_ctx, sizeof(twr.towers.floors[0]) * 5);
+
+ /* what I'd like for christmas ... */
+
+ /* an RPC interface ... */
+ twr.towers.floors[0].lhs.protocol = EPM_PROTOCOL_UUID;
+ GUID_from_string(uuid, &twr.towers.floors[0].lhs.info.uuid.uuid);
+ twr.towers.floors[0].lhs.info.uuid.version = version;
+ twr.towers.floors[0].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
+
+ /* encoded with NDR ... */
+ twr.towers.floors[1].lhs.protocol = EPM_PROTOCOL_UUID;
+ GUID_from_string(NDR_GUID, &twr.towers.floors[1].lhs.info.uuid.uuid);
+ twr.towers.floors[1].lhs.info.uuid.version = NDR_GUID_VERSION;
+ twr.towers.floors[1].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
+
+ /* on an RPC connection ... */
+ twr.towers.floors[2].lhs.protocol = EPM_PROTOCOL_RPC_C;
+ twr.towers.floors[2].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr.towers.floors[2].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
+
+ /* on a TCP port ... */
+ twr.towers.floors[3].lhs.protocol = EPM_PROTOCOL_TCP;
+ twr.towers.floors[3].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr.towers.floors[3].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 2);
+
+ /* on an IP link ... */
+ twr.towers.floors[4].lhs.protocol = EPM_PROTOCOL_IP;
+ twr.towers.floors[4].lhs.info.lhs_data = data_blob(NULL, 0);
+ twr.towers.floors[4].rhs.rhs_data = data_blob_talloc_zero(p->mem_ctx, 4);
+
+ /* with some nice pretty paper around it of course */
+ r.in.object = &guid;
+ r.in.map_tower = &twr;
+ r.in.entry_handle = &handle;
+ r.in.max_towers = 1;
+ r.out.entry_handle = &handle;
+
+ status = dcerpc_epm_Map(p, p->mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ dcerpc_pipe_close(p);
+ return status;
+ }
+ if (r.out.status != 0 || r.out.num_towers != 1) {
+ dcerpc_pipe_close(p);
+ return NT_STATUS_PORT_UNREACHABLE;
+ }
+
+ twr_r = r.out.towers[0].twr;
+ if (!twr_r) {
+ dcerpc_pipe_close(p);
+ return NT_STATUS_PORT_UNREACHABLE;
+ }
+
+ if (twr_r->towers.num_floors != 5 ||
+ twr_r->towers.floors[3].lhs.protocol != twr.towers.floors[3].lhs.protocol ||
+ twr_r->towers.floors[3].rhs.rhs_data.length != 2) {
+ dcerpc_pipe_close(p);
+ return NT_STATUS_PORT_UNREACHABLE;
+ }
+
+ *port = RSVAL(twr_r->towers.floors[3].rhs.rhs_data.data, 0);
+
+ dcerpc_pipe_close(p);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ find the pipe name for a local IDL interface
+*/
+const char *idl_pipe_name(const char *uuid, uint32 if_version)
+{
+ int i;
+ for (i=0;dcerpc_pipes[i];i++) {
+ if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
+ dcerpc_pipes[i]->if_version == if_version) {
+ return dcerpc_pipes[i]->name;
+ }
+ }
+ return "UNKNOWN";
+}
+
+/*
+ find the number of calls defined by local IDL
+*/
+int idl_num_calls(const char *uuid, uint32 if_version)
+{
+ int i;
+ for (i=0;dcerpc_pipes[i];i++) {
+ if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0 &&
+ dcerpc_pipes[i]->if_version == if_version) {
+ return dcerpc_pipes[i]->num_calls;
+ }
+ }
+ return -1;
+}
+
+
+/*
+ find a dcerpc interface by name
+*/
+const struct dcerpc_interface_table *idl_iface_by_name(const char *name)
+{
+ int i;
+ for (i=0;dcerpc_pipes[i];i++) {
+ if (strcasecmp(dcerpc_pipes[i]->name, name) == 0) {
+ return dcerpc_pipes[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ find a dcerpc interface by uuid
+*/
+const struct dcerpc_interface_table *idl_iface_by_uuid(const char *uuid)
+{
+ int i;
+ for (i=0;dcerpc_pipes[i];i++) {
+ if (strcasecmp(dcerpc_pipes[i]->uuid, uuid) == 0) {
+ return dcerpc_pipes[i];
+ }
+ }
+ return NULL;
+}
+
+
+
+/*
+ push a dcerpc_packet into a blob, potentially with auth info
+*/
+NTSTATUS dcerpc_push_auth(DATA_BLOB *blob, TALLOC_CTX *mem_ctx,
+ struct dcerpc_packet *pkt,
+ struct dcerpc_auth *auth_info)
+{
+ NTSTATUS status;
+ struct ndr_push *ndr;
+
+ ndr = ndr_push_init_ctx(mem_ctx);
+ if (!ndr) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
+ ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
+ }
+
+ if (auth_info) {
+ pkt->auth_length = auth_info->credentials.length;
+ } else {
+ pkt->auth_length = 0;
+ }
+
+ status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (auth_info) {
+ status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, auth_info);
+ }
+
+ *blob = ndr_push_blob(ndr);
+
+ /* fill in the frag length */
+ dcerpc_set_frag_length(blob, blob->length);
+
+ return NT_STATUS_OK;
+}
+
+
+static const struct {
+ const char *name;
+ enum dcerpc_transport_t transport;
+} ncacn_transports[] = {
+ {"ncacn_np", NCACN_NP},
+ {"ncacn_ip_tcp", NCACN_IP_TCP}
+};
+
+static const struct {
+ const char *name;
+ uint32 flag;
+} ncacn_options[] = {
+ {"sign", DCERPC_SIGN},
+ {"seal", DCERPC_SEAL},
+ {"schannel", DCERPC_SCHANNEL},
+ {"validate", DCERPC_DEBUG_VALIDATE_BOTH},
+ {"print", DCERPC_DEBUG_PRINT_BOTH},
+ {"bigendian", DCERPC_PUSH_BIGENDIAN}
+};
+
+/*
+ form a binding string from a binding structure
+*/
+const char *dcerpc_binding_string(TALLOC_CTX *mem_ctx, const struct dcerpc_binding *b)
+{
+ char *s = NULL;
+ int i;
+ const char *t_name=NULL;
+
+ for (i=0;i<ARRAY_SIZE(ncacn_transports);i++) {
+ if (ncacn_transports[i].transport == b->transport) {
+ t_name = ncacn_transports[i].name;
+ }
+ }
+ if (!t_name) {
+ return NULL;
+ }
+
+ s = talloc_asprintf(mem_ctx, "%s:%s:[", t_name, b->host);
+ if (!s) return NULL;
+
+ /* this is a *really* inefficent way of dealing with strings,
+ but this is rarely called and the strings are always short,
+ so I don't care */
+ for (i=0;b->options && b->options[i];i++) {
+ s = talloc_asprintf(mem_ctx, "%s%s,", s, b->options[i]);
+ if (!s) return NULL;
+ }
+ for (i=0;i<ARRAY_SIZE(ncacn_options);i++) {
+ if (b->flags & ncacn_options[i].flag) {
+ s = talloc_asprintf(mem_ctx, "%s%s,", s, ncacn_options[i].name);
+ if (!s) return NULL;
+ }
+ }
+ if (s[strlen(s)-1] == ',') {
+ s[strlen(s)-1] = 0;
+ }
+ s = talloc_asprintf(mem_ctx, "%s]", s);
+
+ return s;
+}
+
+/*
+ parse a binding string into a dcerpc_binding structure
+*/
+NTSTATUS dcerpc_parse_binding(TALLOC_CTX *mem_ctx, const char *s, struct dcerpc_binding *b)
+{
+ char *part1, *part2, *part3;
+ char *p;
+ int i, j, comma_count;
+
+ p = strchr(s, ':');
+ if (!p) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ part1 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
+ if (!part1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ s = p+1;
+
+ p = strchr(s, ':');
+ if (!p) {
+ p = strchr(s, '[');
+ if (p) {
+ part2 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
+ part3 = talloc_strdup(mem_ctx, p+1);
+ if (part3[strlen(part3)-1] != ']') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ part3[strlen(part3)-1] = 0;
+ } else {
+ part2 = talloc_strdup(mem_ctx, s);
+ part3 = NULL;
+ }
+ } else {
+ part2 = talloc_strndup(mem_ctx, s, PTR_DIFF(p, s));
+ part3 = talloc_strdup(mem_ctx, p+1);
+ }
+ if (!part2) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<ARRAY_SIZE(ncacn_transports);i++) {
+ if (strcasecmp(part1, ncacn_transports[i].name) == 0) {
+ b->transport = ncacn_transports[i].transport;
+ break;
+ }
+ }
+ if (i==ARRAY_SIZE(ncacn_transports)) {
+ DEBUG(0,("Unknown dcerpc transport '%s'\n", part1));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ b->host = part2;
+ b->options = NULL;
+ b->flags = 0;
+
+ if (!part3) {
+ return NT_STATUS_OK;
+ }
+
+ /* the [] brackets are optional */
+ if (*part3 == '[' && part3[strlen(part3)-1] == ']') {
+ part3++;
+ part3[strlen(part3)-1] = 0;
+ }
+
+ comma_count = count_chars(part3, ',');
+ b->options = talloc_array_p(mem_ctx, const char *, comma_count+2);
+ if (!b->options) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; (p = strchr(part3, ',')); i++) {
+ b->options[i] = talloc_strndup(mem_ctx, part3, PTR_DIFF(p, part3));
+ if (!b->options[i]) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ part3 = p+1;
+ }
+ b->options[i] = part3;
+ b->options[i+1] = NULL;
+
+ /* some options are pre-parsed for convenience */
+ for (i=0;b->options[i];i++) {
+ for (j=0;j<ARRAY_SIZE(ncacn_options);j++) {
+ if (strcasecmp(ncacn_options[j].name, b->options[i]) == 0) {
+ int k;
+ b->flags |= ncacn_options[j].flag;
+ for (k=i;b->options[k];k++) {
+ b->options[k] = b->options[k+1];
+ }
+ i--;
+ break;
+ }
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* open a rpc connection to a rpc pipe on SMB using the binding
+ structure to determine the endpoint and options */
+static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
+ struct dcerpc_binding *binding,
+ const char *pipe_uuid,
+ uint32 pipe_version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ NTSTATUS status;
+ BOOL retry;
+ struct cli_state *cli;
+ const char *pipe_name;
+
+ if (!binding->options || !binding->options[0]) {
+ const struct dcerpc_interface_table *table = idl_iface_by_uuid(pipe_uuid);
+ if (!table) {
+ DEBUG(0,("Unknown interface endpoint '%s'\n", pipe_uuid));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ /* only try the first endpoint for now */
+ pipe_name = table->endpoints->names[0];
+ } else {
+ pipe_name = binding->options[0];
+ }
+
+ if (strncasecmp(pipe_name, "\\pipe\\", 6) == 0) {
+ pipe_name += 6;
+ }
+ if (strncasecmp(pipe_name, "/pipe/", 6) == 0) {
+ pipe_name += 6;
+ }
+
+ status = cli_full_connection(&cli, lp_netbios_name(),
+ binding->host, NULL,
+ "ipc$", "?????",
+ username, username[0]?domain:"",
+ password, 0, &retry);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to connect to %s - %s\n", binding->host, nt_errstr(status)));
+ return status;
+ }
+
+ status = dcerpc_pipe_open_smb(p, cli->tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to open pipe %s - %s\n", pipe_name, nt_errstr(status)));
+ cli_tdis(cli);
+ cli_shutdown(cli);
+ return status;
+ }
+
+ /* this ensures that the reference count is decremented so
+ a pipe close will really close the link */
+ cli_tree_close(cli->tree);
+
+ (*p)->flags = binding->flags;
+
+ if (binding->flags & DCERPC_SCHANNEL) {
+ const char *trust_password = secrets_fetch_machine_password();
+ if (!trust_password) {
+ DEBUG(0,("Unable to fetch machine password\n"));
+ goto done;
+ }
+ status = dcerpc_bind_auth_schannel(*p, pipe_uuid, pipe_version,
+ lp_workgroup(),
+ lp_netbios_name(),
+ trust_password);
+ } else if (binding->flags & (DCERPC_SIGN | DCERPC_SEAL)) {
+ status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
+ } else {
+ status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
+ }
+
+done:
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status)));
+ dcerpc_pipe_close(*p);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/* open a rpc connection to a rpc pipe on SMP using the binding
+ structure to determine the endpoint and options */
+static NTSTATUS dcerpc_pipe_connect_ncacn_ip_tcp(struct dcerpc_pipe **p,
+ struct dcerpc_binding *binding,
+ const char *pipe_uuid,
+ uint32 pipe_version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ NTSTATUS status;
+ uint32 port = 0;
+
+ if (binding->options && binding->options[0]) {
+ port = atoi(binding->options[0]);
+ }
+
+ if (port == 0) {
+ status = dcerpc_epm_map_tcp_port(binding->host,
+ pipe_uuid, pipe_version,
+ &port);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to map DCERPC/TCP port for '%s' - %s\n",
+ pipe_uuid, nt_errstr(status)));
+ return status;
+ }
+ DEBUG(1,("Mapped to DCERPC/TCP port %u\n", port));
+ }
+
+ status = dcerpc_pipe_open_tcp(p, binding->host, port);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to connect to %s:%d\n", binding->host, port));
+ return status;
+ }
+
+ /* it doesn't seem to work to do a null NTLMSSP session without either sign
+ or seal, so force signing if we are doing ntlmssp */
+ if (username[0] && !(binding->flags & (DCERPC_SIGN|DCERPC_SEAL))) {
+ binding->flags |= DCERPC_SIGN;
+ }
+
+ (*p)->flags = binding->flags;
+
+ if (!(binding->flags & (DCERPC_SIGN|DCERPC_SEAL)) && !username[0]) {
+ status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
+ } else {
+ status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version,
+ domain, username, password);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to bind to uuid %s - %s\n", pipe_uuid, nt_errstr(status)));
+ dcerpc_pipe_close(*p);
+ return status;
+ }
+
+ return status;
+}
+
+
+/* open a rpc connection to a rpc pipe, using the specified
+ binding structure to determine the endpoint and options */
+NTSTATUS dcerpc_pipe_connect_b(struct dcerpc_pipe **p,
+ struct dcerpc_binding *binding,
+ const char *pipe_uuid,
+ uint32 pipe_version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
+
+ switch (binding->transport) {
+ case NCACN_NP:
+ status = dcerpc_pipe_connect_ncacn_np(p, binding, pipe_uuid, pipe_version,
+ domain, username, password);
+ break;
+ case NCACN_IP_TCP:
+ status = dcerpc_pipe_connect_ncacn_ip_tcp(p, binding, pipe_uuid, pipe_version,
+ domain, username, password);
+ break;
+ }
+
+ /* remember the binding string for possible secondary connections */
+ if (NT_STATUS_IS_OK(status)) {
+ (*p)->binding_string = dcerpc_binding_string((*p)->mem_ctx, binding);
+ }
+
+ return status;
+}
+
+
+/* open a rpc connection to a rpc pipe, using the specified string
+ binding to determine the endpoint and options */
+NTSTATUS dcerpc_pipe_connect(struct dcerpc_pipe **p,
+ const char *binding,
+ const char *pipe_uuid,
+ uint32 pipe_version,
+ const char *domain,
+ const char *username,
+ const char *password)
+{
+ struct dcerpc_binding b;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_init("dcerpc_pipe_connect");
+ if (!mem_ctx) return NT_STATUS_NO_MEMORY;
+
+ status = dcerpc_parse_binding(mem_ctx, binding, &b);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to parse dcerpc binding '%s'\n", binding));
+ talloc_destroy(mem_ctx);
+ return status;
+ }
+
+ DEBUG(3,("Using binding %s\n", dcerpc_binding_string(mem_ctx, &b)));
+
+ status = dcerpc_pipe_connect_b(p, &b, pipe_uuid, pipe_version, domain, username, password);
+
+ talloc_destroy(mem_ctx);
+ return status;
+}
+
+
+/*
+ create a secondary dcerpc connection on SMB
+ the secondary connection will be on the same SMB connection, but
+ use a new fnum
+*/
+NTSTATUS dcerpc_secondary_smb(struct dcerpc_pipe *p, struct dcerpc_pipe **p2,
+ const char *pipe_name,
+ const char *pipe_uuid,
+ uint32 pipe_version)
+{
+ NTSTATUS status;
+ struct cli_tree *tree;
+
+ tree = dcerpc_smb_tree(p);
+ if (!tree) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_pipe_open_smb(p2, tree, pipe_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ (*p2)->flags = p->flags;
+
+ status = dcerpc_bind_auth_none(*p2, pipe_uuid, pipe_version);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}