From e0ac659917066dbf7f8fdbcc7684ce2b49dd04d9 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 26 Nov 2003 01:16:41 +0000 Subject: signed DCERPC over TCP now works ! * moved ntlmssp code into libcli/auth/, and updated to latest ntlmssp code from samba3 (thanks Andrew! the new interface is great) * added signing/ntlmssp support in the dcerpc code * added a dcerpc_auth.c module for the various dcerpc auth mechanisms (This used to be commit c18c9b5585a3e5f7868562820c14f7cb529cdbcd) --- source4/librpc/idl/dcerpc.idl | 70 ++++++++--- source4/librpc/ndr/ndr.c | 51 ++++---- source4/librpc/ndr/ndr_basic.c | 64 +++------- source4/librpc/rpc/dcerpc.c | 246 ++++++++++++++++++++++++++++++++++----- source4/librpc/rpc/dcerpc.h | 2 + source4/librpc/rpc/dcerpc_auth.c | 130 +++++++++++++++++++++ source4/librpc/rpc/dcerpc_smb.c | 11 +- source4/librpc/rpc/dcerpc_tcp.c | 13 +-- 8 files changed, 443 insertions(+), 144 deletions(-) create mode 100644 source4/librpc/rpc/dcerpc_auth.c (limited to 'source4/librpc') diff --git a/source4/librpc/idl/dcerpc.idl b/source4/librpc/idl/dcerpc.idl index c45309ba68..978bc5640c 100644 --- a/source4/librpc/idl/dcerpc.idl +++ b/source4/librpc/idl/dcerpc.idl @@ -28,7 +28,7 @@ interface dcerpc uint8 num_contexts; dcerpc_ctx_list ctx_list[num_contexts]; [flag(NDR_ALIGN8)] DATA_BLOB _pad; - [flag(NDR_REMAINING)] DATA_BLOB auth_verifier; + [flag(NDR_REMAINING)] DATA_BLOB auth_info; } dcerpc_bind; typedef struct { @@ -53,8 +53,7 @@ interface dcerpc [flag(NDR_ALIGN4)] DATA_BLOB _pad1; uint8 num_results; dcerpc_ack_ctx ctx_list[num_results]; - [flag(NDR_ALIGN8)] DATA_BLOB _pad2; - [flag(NDR_REMAINING)] DATA_BLOB auth_verifier; + [flag(NDR_REMAINING)] DATA_BLOB auth_info; } dcerpc_bind_ack; typedef struct { @@ -75,21 +74,64 @@ interface dcerpc uint32 status; } dcerpc_fault; + + const uint8 DCERPC_AUTH_TYPE_NONE = 0; + const uint8 DCERPC_AUTH_TYPE_KRB5 = 1; + const uint8 DCERPC_AUTH_TYPE_NTLMSSP = 10; + + const uint8 DCERPC_AUTH_LEVEL_NONE = 1; + const uint8 DCERPC_AUTH_LEVEL_CONNECT = 2; + const uint8 DCERPC_AUTH_LEVEL_CALL = 3; + const uint8 DCERPC_AUTH_LEVEL_PACKET = 4; + const uint8 DCERPC_AUTH_LEVEL_INTEGRITY = 5; + const uint8 DCERPC_AUTH_LEVEL_PRIVACY = 6; + + typedef [public] struct { + uint8 auth_type; + uint8 auth_level; + uint8 auth_pad_length; + uint8 auth_reserved; + uint32 auth_context_id; + [flag(NDR_REMAINING)] DATA_BLOB credentials; + } dcerpc_auth; + + typedef [public] struct { + uint32 _pad; + [flag(NDR_REMAINING)] DATA_BLOB auth_info; + } dcerpc_auth3; + typedef enum { - DCERPC_PKT_REQUEST=0, - DCERPC_PKT_RESPONSE=2, - DCERPC_PKT_FAULT=3, - DCERPC_PKT_BIND=11, - DCERPC_PKT_BIND_ACK=12, - DCERPC_PKT_BIND_NAK=13 + DCERPC_PKT_REQUEST = 0, + DCERPC_PKT_PING = 1, + DCERPC_PKT_RESPONSE = 2, + DCERPC_PKT_FAULT = 3, + DCERPC_PKT_WORKING = 4, + DCERPC_PKT_NOCALL = 5, + DCERPC_PKT_REJECT = 6, + DCERPC_PKT_ACK = 7, + DCERPC_PKT_CL_CANCEL = 8, + DCERPC_PKT_FACK = 9, + DCERPC_PKT_CANCEL_ACK = 10, + DCERPC_PKT_BIND = 11, + DCERPC_PKT_BIND_ACK = 12, + DCERPC_PKT_BIND_NAK = 13, + DCERPC_PKT_ALTER = 14, + DCERPC_PKT_ALTER_ACK = 15, + DCERPC_PKT_AUTH3 = 16, + DCERPC_PKT_SHUTDOWN = 17, + DCERPC_PKT_CO_CANCEL = 18, + DCERPC_PKT_ORPHANED = 19 } dcerpc_pkt_type; typedef [nodiscriminant] union { - [case(DCERPC_PKT_REQUEST)] dcerpc_request request; - [case(DCERPC_PKT_RESPONSE)] dcerpc_response response; - [case(DCERPC_PKT_BIND)] dcerpc_bind bind; - [case(DCERPC_PKT_BIND_ACK)] dcerpc_bind_ack bind_ack; - [case(DCERPC_PKT_FAULT)] dcerpc_fault fault; + [case(DCERPC_PKT_REQUEST)] dcerpc_request request; + [case(DCERPC_PKT_RESPONSE)] dcerpc_response response; + [case(DCERPC_PKT_BIND)] dcerpc_bind bind; + [case(DCERPC_PKT_BIND_ACK)] dcerpc_bind_ack bind_ack; + [case(DCERPC_PKT_ALTER)] dcerpc_bind alter; + [case(DCERPC_PKT_ALTER_ACK)] dcerpc_bind_ack alter_ack; + [case(DCERPC_PKT_FAULT)] dcerpc_fault fault; + [case(DCERPC_PKT_AUTH3)] dcerpc_auth3 auth; } dcerpc_payload; diff --git a/source4/librpc/ndr/ndr.c b/source4/librpc/ndr/ndr.c index 6116150cea..9915ba4212 100644 --- a/source4/librpc/ndr/ndr.c +++ b/source4/librpc/ndr/ndr.c @@ -31,12 +31,14 @@ #define NDR_BASE_MARSHALL_SIZE 1024 +/* + only include interfaces that contain callable dcerpc functions here +*/ const struct dcerpc_interface_table *dcerpc_pipes[] = { &dcerpc_table_samr, &dcerpc_table_lsarpc, &dcerpc_table_netdfs, &dcerpc_table_atsvc, - &dcerpc_table_dcerpc, &dcerpc_table_rpcecho, &dcerpc_table_epmapper, &dcerpc_table_eventlog, @@ -93,31 +95,6 @@ NTSTATUS ndr_pull_subcontext(struct ndr_pull *ndr, struct ndr_pull *ndr2, uint32 } -/* limit the remaining size of the current ndr parse structure to the - given size, starting at the given offset - - this is used when a ndr packet has an explicit size on the wire, and we - need to make sure that we don't use more data than is indicated - - the 'ofs' parameter indicates how many bytes back from the current - offset in the buffer the 'size' number of bytes starts -*/ -NTSTATUS ndr_pull_limit_size(struct ndr_pull *ndr, uint32 size, uint32 ofs) -{ - uint32 new_size; - new_size = ndr->offset + size - ofs; - - if (new_size > ndr->data_size) { - return ndr_pull_error(ndr, NDR_ERR_BUFSIZE, - "ndr_pull_limit_size %s %u failed", - size, ofs); - } - ndr->data_size = new_size; - - return NT_STATUS_OK; -} - - /* advance by 'size' bytes */ @@ -780,7 +757,7 @@ NTSTATUS ndr_pull_union_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, uint32 level, pull a struct from a blob using NDR */ NTSTATUS ndr_pull_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, - NTSTATUS (*fn)(struct ndr_pull *, int ndr_flags, void *)) + NTSTATUS (*fn)(struct ndr_pull *, int , void *)) { struct ndr_pull *ndr; ndr = ndr_pull_init_blob(blob, mem_ctx); @@ -790,4 +767,24 @@ NTSTATUS ndr_pull_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, return fn(ndr, NDR_SCALARS|NDR_BUFFERS, p); } +/* + push a struct to a blob using NDR +*/ +NTSTATUS ndr_push_struct_blob(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, void *p, + NTSTATUS (*fn)(struct ndr_push *, int , void *)) +{ + NTSTATUS status; + struct ndr_push *ndr; + ndr = ndr_push_init_ctx(mem_ctx); + if (!ndr) { + return NT_STATUS_NO_MEMORY; + } + status = fn(ndr, NDR_SCALARS|NDR_BUFFERS, p); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *blob = ndr_push_blob(ndr); + + return NT_STATUS_OK; +} diff --git a/source4/librpc/ndr/ndr_basic.c b/source4/librpc/ndr/ndr_basic.c index 1f78bc17b6..52f4d29428 100644 --- a/source4/librpc/ndr/ndr_basic.c +++ b/source4/librpc/ndr/ndr_basic.c @@ -256,6 +256,17 @@ NTSTATUS ndr_push_bytes(struct ndr_push *ndr, const char *data, uint32 n) return NT_STATUS_OK; } +/* + push some zero bytes +*/ +NTSTATUS ndr_push_zero(struct ndr_push *ndr, uint32 n) +{ + NDR_PUSH_NEED_BYTES(ndr, n); + memset(ndr->data + ndr->offset, 0, n); + ndr->offset += n; + return NT_STATUS_OK; +} + /* push an array of uint8 */ @@ -298,27 +309,6 @@ void ndr_push_restore(struct ndr_push *ndr, struct ndr_push_save *save) ndr->offset = save->offset; } -/* - this is used when a packet has a 4 byte length field. We remember the start position - and come back to it later to fill in the size -*/ -NTSTATUS ndr_push_length4_start(struct ndr_push *ndr, struct ndr_push_save *save) -{ - NDR_PUSH_ALIGN(ndr, 4); - ndr_push_save(ndr, save); - return ndr_push_uint32(ndr, 0); -} - -NTSTATUS ndr_push_length4_end(struct ndr_push *ndr, struct ndr_push_save *save) -{ - struct ndr_push_save save2; - ndr_push_save(ndr, &save2); - ndr_push_restore(ndr, save); - NDR_CHECK(ndr_push_uint32(ndr, save2.offset - ndr->offset)); - ndr_push_restore(ndr, &save2); - return NT_STATUS_OK; -} - /* push a 1 if a pointer is non-NULL, otherwise 0 */ @@ -576,34 +566,6 @@ NTSTATUS ndr_push_string(struct ndr_push *ndr, int ndr_flags, const char *s) return NT_STATUS_OK; } -/* - push a 4 byte offset pointer, remembering where we are so we can later fill - in the correct value -*/ -NTSTATUS ndr_push_offset(struct ndr_push *ndr, struct ndr_push_save *ofs) -{ - NDR_PUSH_ALIGN(ndr, 4); - ndr_push_save(ndr, ofs); - return ndr_push_uint32(ndr, 0); -} - -/* - fill in the correct offset in a saved offset pointer - the offset is taken relative to 'save' -*/ -NTSTATUS ndr_push_offset_ptr(struct ndr_push *ndr, - struct ndr_push_save *ofs, - struct ndr_push_save *save) -{ - struct ndr_push_save save2; - ndr_push_save(ndr, &save2); - ndr_push_restore(ndr, ofs); - NDR_CHECK(ndr_push_uint32(ndr, save2.offset - save->offset)); - ndr_push_restore(ndr, &save2); - return NT_STATUS_OK; -} - - /* push a GUID */ @@ -761,7 +723,9 @@ NTSTATUS GUID_from_string(const char *s, struct GUID *guid) return NT_STATUS_OK; } - +/* + its useful to be able to display these in debugging messages +*/ const char *GUID_string(TALLOC_CTX *mem_ctx, const struct GUID *guid) { return talloc_asprintf(mem_ctx, diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 012677a122..bf5da4edb4 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -41,6 +41,8 @@ struct dcerpc_pipe *dcerpc_pipe_init(void) p->reference_count = 0; p->mem_ctx = mem_ctx; p->call_id = 1; + p->auth_info = NULL; + p->ntlmssp_state = NULL; return p; } @@ -61,7 +63,8 @@ void dcerpc_pipe_close(struct dcerpc_pipe *p) parse a data blob into a dcerpc_packet structure. This handles both input and output packets */ -NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt) +static NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct dcerpc_packet *pkt) { struct ndr_pull *ndr; @@ -73,32 +76,169 @@ NTSTATUS dcerpc_pull(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet 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->ntlmssp_state) { + return dcerpc_pull(blob, mem_ctx, pkt); + } + + ndr = ndr_pull_init_blob(blob, mem_ctx); + if (!ndr) { + return NT_STATUS_NO_MEMORY; + } + + /* 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; + } + + status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* check the signature */ + status = ntlmssp_check_packet(p->ntlmssp_state, + pkt->u.response.stub_and_verifier.data, + pkt->u.response.stub_and_verifier.length, + &auth.credentials); + + /* 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_packet into a blob. This handles both input and output packets */ -NTSTATUS dcerpc_push(DATA_BLOB *blob, TALLOC_CTX *mem_ctx, struct dcerpc_packet *pkt) +static NTSTATUS dcerpc_push(struct dcerpc_pipe *p, + DATA_BLOB *blob, TALLOC_CTX *mem_ctx, + struct dcerpc_packet *pkt) { - struct ndr_push *ndr; NTSTATUS status; + struct ndr_push *ndr; ndr = ndr_push_init_ctx(mem_ctx); if (!ndr) { return NT_STATUS_NO_MEMORY; } + if (p->auth_info) { + pkt->auth_length = p->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 (p->auth_info) { + status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, + p->auth_info); + } + *blob = ndr_push_blob(ndr); /* fill in the frag length */ SSVAL(blob->data, 8, blob->length); - return status; + return NT_STATUS_OK; +} + + +/* + 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->ntlmssp_state) { + return dcerpc_push(p, blob, mem_ctx, pkt); + } + + ndr = ndr_push_init_ctx(mem_ctx); + if (!ndr) { + return NT_STATUS_NO_MEMORY; + } + + 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 the packet */ + status = ntlmssp_sign_packet(p->ntlmssp_state, + ndr->data+24, ndr->offset-24, + &p->auth_info->credentials); + 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 */ + SSVAL(blob->data, 8, blob->length); + SSVAL(blob->data, 10, p->auth_info->credentials.length); + + return NT_STATUS_OK; } @@ -118,28 +258,25 @@ static void init_dcerpc_hdr(struct dcerpc_packet *pkt) /* 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) { - TALLOC_CTX *mem_ctx; struct dcerpc_packet pkt; NTSTATUS status; DATA_BLOB blob; - DATA_BLOB blob_out; struct dcerpc_syntax_id tsyntax; - mem_ctx = talloc_init("dcerpc_bind"); - if (!mem_ctx) { - return NT_STATUS_NO_MEMORY; - } - init_dcerpc_hdr(&pkt); pkt.ptype = DCERPC_PKT_BIND; pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST; - pkt.call_id = p->call_id++; + pkt.call_id = p->call_id; pkt.auth_length = 0; pkt.u.bind.max_xmit_frag = 0x2000; @@ -148,7 +285,6 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 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) { - talloc_destroy(mem_ctx); return NT_STATUS_NO_MEMORY; } pkt.u.bind.ctx_list[0].context_id = 0; @@ -156,23 +292,23 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, 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_verifier = data_blob(NULL, 0); + pkt.u.bind.auth_info = data_blob(NULL, 0); - status = dcerpc_push(&blob, mem_ctx, &pkt); + /* construct the NDR form of the packet */ + status = dcerpc_push(p, &blob, mem_ctx, &pkt); if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); return status; } - status = p->transport.full_request(p, mem_ctx, &blob, &blob_out); + /* send it on its way */ + status = p->transport.full_request(p, mem_ctx, &blob, &blob); if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); return status; } - status = dcerpc_pull(&blob_out, mem_ctx, &pkt); + /* unmarshall the NDR */ + status = dcerpc_pull(&blob, mem_ctx, &pkt); if (!NT_STATUS_IS_OK(status)) { - talloc_destroy(mem_ctx); return status; } @@ -185,13 +321,55 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p, p->srv_max_xmit_frag = pkt.u.bind_ack.max_xmit_frag; p->srv_max_recv_frag = pkt.u.bind_ack.max_recv_frag; - talloc_destroy(mem_ctx); + /* 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 bind using the given UUID and version */ +/* + 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(&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(p, &blob, mem_ctx, &pkt); + 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; @@ -215,7 +393,7 @@ NTSTATUS dcerpc_bind_byuuid(struct dcerpc_pipe *p, transfer_syntax.major_version = 2; transfer_syntax.minor_version = 0; - return dcerpc_bind(p, &syntax, &transfer_syntax); + return dcerpc_bind(p, mem_ctx, &syntax, &transfer_syntax); } /* @@ -230,7 +408,7 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, struct dcerpc_packet pkt; NTSTATUS status; - DATA_BLOB blob_in, blob_out, payload; + DATA_BLOB blob, payload; uint32 remaining, chunk_size; init_dcerpc_hdr(&pkt); @@ -261,12 +439,12 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, (stub_data_in->length - remaining); pkt.u.request.stub_and_verifier.length = chunk_size; - status = dcerpc_push(&blob_in, mem_ctx, &pkt); + 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_in); + status = p->transport.initial_request(p, mem_ctx, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -285,15 +463,18 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, (stub_data_in->length - remaining); pkt.u.request.stub_and_verifier.length = remaining; - status = dcerpc_push(&blob_in, mem_ctx, &pkt); + 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_in, &blob_out); + status = p->transport.full_request(p, mem_ctx, &blob, &blob); + if (!NT_STATUS_IS_OK(status)) { + return status; + } - status = dcerpc_pull(&blob_out, mem_ctx, &pkt); + status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -317,12 +498,12 @@ NTSTATUS dcerpc_request(struct dcerpc_pipe *p, while (!(pkt.pfc_flags & DCERPC_PFC_FLAG_LAST)) { uint32 length; - status = p->transport.secondary_request(p, mem_ctx, &blob_out); + status = p->transport.secondary_request(p, mem_ctx, &blob); if (!NT_STATUS_IS_OK(status)) { return status; } - status = dcerpc_pull(&blob_out, mem_ctx, &pkt); + status = dcerpc_pull_request_sign(p, &blob, mem_ctx, &pkt); if (!NT_STATUS_IS_OK(status)) { return status; } @@ -568,6 +749,9 @@ NTSTATUS dcerpc_ndr_request(struct dcerpc_pipe *p, 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); diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h index e0e7c8bd5a..e41b998d90 100644 --- a/source4/librpc/rpc/dcerpc.h +++ b/source4/librpc/rpc/dcerpc.h @@ -36,6 +36,8 @@ struct dcerpc_pipe { uint32 srv_max_xmit_frag; uint32 srv_max_recv_frag; unsigned flags; + struct ntlmssp_state *ntlmssp_state; + struct dcerpc_auth *auth_info; struct dcerpc_transport { void *private; diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c new file mode 100644 index 0000000000..32fdcb0b86 --- /dev/null +++ b/source4/librpc/rpc/dcerpc_auth.c @@ -0,0 +1,130 @@ +/* + 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 simple 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; + + 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; + 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->ntlmssp_state = NULL; + + status = ntlmssp_update(state, + p->auth_info->credentials, + &p->auth_info->credentials); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + 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, + &p->auth_info->credentials); + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + goto done; + } + + status = dcerpc_auth3(p, mem_ctx); + p->ntlmssp_state = state; + p->auth_info->credentials = data_blob(NULL, 0); + + ntlmssp_sign_init(state); + +done: + talloc_destroy(mem_ctx); + + if (!NT_STATUS_IS_OK(status)) { + p->ntlmssp_state = NULL; + } + + return status; +} + + +/* + 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/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c index 6af997275c..9acae00249 100644 --- a/source4/librpc/rpc/dcerpc_smb.c +++ b/source4/librpc/rpc/dcerpc_smb.c @@ -301,9 +301,7 @@ static const char *smb_peer_name(struct dcerpc_pipe *p) */ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe **p, struct cli_tree *tree, - const char *pipe_name, - const char *pipe_uuid, - uint32 pipe_version) + const char *pipe_name) { struct smb_private *smb; NTSTATUS status; @@ -375,12 +373,5 @@ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe **p, (*p)->transport.private = smb; tree->reference_count++; - /* bind to the pipe, using the uuid as the key */ - status = dcerpc_bind_byuuid(*p, pipe_uuid, pipe_version); - - if (!NT_STATUS_IS_OK(status)) { - dcerpc_pipe_close(*p); - } - return NT_STATUS_OK; } diff --git a/source4/librpc/rpc/dcerpc_tcp.c b/source4/librpc/rpc/dcerpc_tcp.c index ec94baf583..b3523e6855 100644 --- a/source4/librpc/rpc/dcerpc_tcp.c +++ b/source4/librpc/rpc/dcerpc_tcp.c @@ -146,12 +146,9 @@ static const char *tcp_peer_name(struct dcerpc_pipe *p) */ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, const char *server, - uint32 port, - const char *pipe_uuid, - uint32 pipe_version) + uint32 port) { struct tcp_private *tcp; - NTSTATUS status; int fd; struct in_addr addr; @@ -194,13 +191,5 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p, (*p)->transport.private = tcp; - /* bind to the pipe, using the uuid as the key */ - status = dcerpc_bind_byuuid(*p, pipe_uuid, pipe_version); - - if (!NT_STATUS_IS_OK(status)) { - dcerpc_pipe_close(*p); - return status; - } - return NT_STATUS_OK; } -- cgit