From fffd741a7a00b07c85eec254b8cc7c2fe40cef18 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 26 Nov 2003 03:36:17 +0000 Subject: added auto-determination of the DCERPC over TCP port number by asking the servers endpoint mapper (This used to be commit 4abf5376b00f580eb69196e55a792cc7eb4c9880) --- source4/Makefile.in | 1 + source4/build/pidl/idl.gram | 2 +- source4/librpc/idl/dcerpc.idl | 9 +++ source4/librpc/idl/epmapper.idl | 10 ++- source4/librpc/rpc/dcerpc.c | 14 ++-- source4/librpc/rpc/dcerpc_util.c | 149 +++++++++++++++++++++++++++++++++++++++ source4/torture/torture.c | 20 ++++-- 7 files changed, 191 insertions(+), 14 deletions(-) create mode 100644 source4/librpc/rpc/dcerpc_util.c (limited to 'source4') diff --git a/source4/Makefile.in b/source4/Makefile.in index 4f7b20837c..c6b763137e 100644 --- a/source4/Makefile.in +++ b/source4/Makefile.in @@ -200,6 +200,7 @@ LIBRAW_NDR_OBJ = librpc/ndr/ndr.o librpc/ndr/ndr_basic.o librpc/ndr/ndr_sec.o \ librpc/gen_ndr/ndr_mgmt.o LIBRAW_RPC_OBJ = librpc/rpc/dcerpc.o librpc/rpc/dcerpc_auth.o \ + librpc/rpc/dcerpc_util.o \ librpc/rpc/dcerpc_smb.o librpc/rpc/dcerpc_tcp.o \ librpc/gen_rpc/rpc_echo.o librpc/gen_rpc/rpc_lsa.o \ librpc/gen_rpc/rpc_dfs.o librpc/gen_rpc/rpc_spoolss.o \ diff --git a/source4/build/pidl/idl.gram b/source4/build/pidl/idl.gram index 5cba06f014..897a5ad769 100644 --- a/source4/build/pidl/idl.gram +++ b/source4/build/pidl/idl.gram @@ -166,7 +166,7 @@ type : text: /[\w\s\..?-]*/ -text2: /[\|\w\s,\*\>"\/\..?-]*/ +text2: /[\|\w\s,\*&\>"\/\..?-]*/ anytext: text2 '(' anytext ')' anytext {{ "$item[1]($item[4])$item[6]" }} diff --git a/source4/librpc/idl/dcerpc.idl b/source4/librpc/idl/dcerpc.idl index 978bc5640c..c23553c444 100644 --- a/source4/librpc/idl/dcerpc.idl +++ b/source4/librpc/idl/dcerpc.idl @@ -31,6 +31,9 @@ interface dcerpc [flag(NDR_REMAINING)] DATA_BLOB auth_info; } dcerpc_bind; + + const uint8 DCERPC_REQUEST_LENGTH = 24; + typedef struct { uint32 alloc_hint; uint16 context_id; @@ -62,6 +65,8 @@ interface dcerpc uint32 versions[num_versions]; } dcerpc_bind_nak; + const uint8 DCERPC_RESPONSE_LENGTH = 24; + typedef struct { uint32 alloc_hint; uint16 context_id; @@ -140,6 +145,10 @@ interface dcerpc const uint8 DCERPC_PFC_FLAG_LAST = 0x02; const uint8 DCERPC_PFC_FLAG_NOCALL = 0x20; + /* these offsets are needed by the signing code */ + const uint8 DCERPC_FRAG_LEN_OFFSET = 8; + const uint8 DCERPC_AUTH_LEN_OFFSET = 10; + typedef [public] struct { uint8 rpc_vers; /* RPC version */ uint8 rpc_vers_minor; /* Minor version */ diff --git a/source4/librpc/idl/epmapper.idl b/source4/librpc/idl/epmapper.idl index 172c83149b..6fa9a7bd60 100644 --- a/source4/librpc/idl/epmapper.idl +++ b/source4/librpc/idl/epmapper.idl @@ -22,12 +22,20 @@ interface epmapper /* this guid indicates NDR encoding in a protocol tower */ const string NDR_GUID = "8a885d04-1ceb-11c9-9fe8-08002b104860"; + const string NDR_GUID_VERSION = 1; typedef struct { GUID uuid; uint16 version; } epm_prot_uuid; + typedef enum { + EPM_PROTOCOL_TCP = 0x07, + EPM_PROTOCOL_IP = 0x09, + EPM_PROTOCOL_RPC_C = 0x0b, + EPM_PROTOCOL_UUID = 0x0d + } epm_protocols; + typedef [nodiscriminant] union { [case(13)] epm_prot_uuid uuid; [default] [flag(NDR_REMAINING)] DATA_BLOB lhs_data; @@ -58,7 +66,7 @@ interface epmapper } epm_towers; typedef struct { - uint32 tower_length; + [value(ndr_size_epm_towers(&r->towers))] uint32 tower_length; [subcontext(4)] epm_towers towers; } epm_twr_t; diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 83fb0b592c..2fc940314d 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -43,6 +43,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) p->call_id = 1; p->auth_info = NULL; p->ntlmssp_state = NULL; + p->flags = 0; return p; } @@ -222,7 +223,8 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, /* sign the packet */ status = ntlmssp_sign_packet(p->ntlmssp_state, - ndr->data+24, ndr->offset-24, + ndr->data + DCERPC_REQUEST_LENGTH, + ndr->offset - DCERPC_REQUEST_LENGTH, &p->auth_info->credentials); if (!NT_STATUS_IS_OK(status)) { return status; @@ -237,9 +239,11 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p, /* extract the whole packet as a blob */ *blob = ndr_push_blob(ndr); - /* fill in the fragment length and auth_length */ - SSVAL(blob->data, 8, blob->length); - SSVAL(blob->data, 10, p->auth_info->credentials.length); + /* 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) */ + SSVAL(blob->data, DCERPC_FRAG_LEN_OFFSET, blob->length); + SSVAL(blob->data, DCERPC_AUTH_LEN_OFFSET, p->auth_info->credentials.length); data_blob_free(&p->auth_info->credentials); @@ -422,7 +426,7 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, /* we can write a full max_recv_frag size, minus the dcerpc request header size */ - chunk_size = p->srv_max_recv_frag - 24; + chunk_size = p->srv_max_recv_frag - DCERPC_REQUEST_LENGTH; pkt.ptype = DCERPC_PKT_REQUEST; pkt.call_id = p->call_id++; diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c new file mode 100644 index 0000000000..75799c1a0b --- /dev/null +++ b/source4/librpc/rpc/dcerpc_util.c @@ -0,0 +1,149 @@ +/* + 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;inum_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; + GUID guid; + struct epm_twr_t twr, *twr_r; + + status = dcerpc_pipe_open_tcp(&p, server, 135); + 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(p->mem_ctx, NULL, 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 = 2; + twr.towers.floors[1].rhs.rhs_data = data_blob_talloc(p->mem_ctx, NULL, 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(p->mem_ctx, NULL, 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(p->mem_ctx, NULL, 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(p->mem_ctx, NULL, 4); + + 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 != EPM_PROTOCOL_TCP || + 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; +} diff --git a/source4/torture/torture.c b/source4/torture/torture.c index 11b27f12eb..18d339d65e 100644 --- a/source4/torture/torture.c +++ b/source4/torture/torture.c @@ -138,11 +138,21 @@ static NTSTATUS torture_rpc_tcp(struct dcerpc_pipe **p, { NTSTATUS status; char *host = lp_parm_string(-1, "torture", "host"); - const char *port = lp_parm_string(-1, "torture", "share"); + const char *port_str = lp_parm_string(-1, "torture", "share"); + uint32 port = atoi(port_str); + + if (port == 0) { + status = dcerpc_epm_map_tcp_port(host, + pipe_uuid, pipe_version, + &port); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } - DEBUG(2,("Connecting to dcerpc server %s:%s\n", host, port)); + DEBUG(2,("Connecting to dcerpc server %s:%u\n", host, port)); - status = dcerpc_pipe_open_tcp(p, host, atoi(port)); + status = dcerpc_pipe_open_tcp(p, host, port); if (!NT_STATUS_IS_OK(status)) { printf("Open of pipe '%s' failed with error (%s)\n", pipe_name, nt_errstr(status)); @@ -202,14 +212,10 @@ NTSTATUS torture_rpc_connection(struct dcerpc_pipe **p, } /* bind to the pipe, using the uuid as the key */ -#if 1 status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, lp_workgroup(), lp_parm_string(-1, "torture", "username"), lp_parm_string(-1, "torture", "password")); -#else - status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version); -#endif if (!NT_STATUS_IS_OK(status)) { dcerpc_pipe_close(*p); return status; -- cgit