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/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 +-- 5 files changed, 349 insertions(+), 53 deletions(-) create mode 100644 source4/librpc/rpc/dcerpc_auth.c (limited to 'source4/librpc/rpc') 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