summaryrefslogtreecommitdiff
path: root/source4/librpc
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2004-09-11 15:11:36 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:58:39 -0500
commit909c9b681a0718b8701e05addbad08c0aec87113 (patch)
treed569f2f54075a0514fa6e4b591d383f47e8d631d /source4/librpc
parent4490c816a90871cd93b1a709bae91e5176e4f599 (diff)
downloadsamba-909c9b681a0718b8701e05addbad08c0aec87113.tar.gz
samba-909c9b681a0718b8701e05addbad08c0aec87113.tar.bz2
samba-909c9b681a0718b8701e05addbad08c0aec87113.zip
r2284: Thanks to some great detective work by tridge, NTLM2 signing now works.
This means that 'require NTLMv2 session security' now works for RPC pipe signing. We don't yet have sealing, but it can't be much further. This is almost all tridge's code, munged into a form that can work with the GENSEC API. This commit also includes more lsakey fixes - that key is used for all DCE-RPC level authenticated connections, even over CIFS/ncacn_np. No doubt I missed something, but I'm going to get some sleep :-) Andrew Bartlett (This used to be commit a1fe175eec884280fb7e9ca8f528134cf4600beb)
Diffstat (limited to 'source4/librpc')
-rw-r--r--source4/librpc/rpc/dcerpc.c126
-rw-r--r--source4/librpc/rpc/dcerpc.h5
-rw-r--r--source4/librpc/rpc/dcerpc_auth.c8
-rw-r--r--source4/librpc/rpc/dcerpc_schannel.c7
-rw-r--r--source4/librpc/rpc/dcerpc_smb.c5
-rw-r--r--source4/librpc/rpc/dcerpc_tcp.c21
-rw-r--r--source4/librpc/rpc/dcerpc_util.c17
7 files changed, 109 insertions, 80 deletions
diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c
index 0595d5eade..c2f691aa09 100644
--- a/source4/librpc/rpc/dcerpc.c
+++ b/source4/librpc/rpc/dcerpc.c
@@ -36,6 +36,7 @@ struct dcerpc_pipe *dcerpc_pipe_init(void)
p->reference_count = 0;
p->call_id = 1;
p->security_state.auth_info = NULL;
+ p->security_state.session_key = dcerpc_generic_session_key;
p->security_state.generic_state = NULL;
p->binding_string = NULL;
p->flags = 0;
@@ -206,8 +207,8 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
if (!NT_STATUS_IS_OK(status)) {
return status;
}
-
-
+
+
/* check signature or unseal the packet */
switch (p->security_state.auth_info->auth_level) {
case DCERPC_AUTH_LEVEL_PRIVACY:
@@ -215,6 +216,8 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
mem_ctx,
pkt->u.response.stub_and_verifier.data,
pkt->u.response.stub_and_verifier.length,
+ blob->data,
+ blob->length - auth.credentials.length,
&auth.credentials);
break;
@@ -223,9 +226,11 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
mem_ctx,
pkt->u.response.stub_and_verifier.data,
pkt->u.response.stub_and_verifier.length,
+ blob->data,
+ blob->length - auth.credentials.length,
&auth.credentials);
break;
-
+
case DCERPC_AUTH_LEVEL_NONE:
break;
@@ -233,7 +238,7 @@ static NTSTATUS dcerpc_pull_request_sign(struct dcerpc_pipe *p,
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;
@@ -253,6 +258,7 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
{
NTSTATUS status;
struct ndr_push *ndr;
+ DATA_BLOB creds2;
/* non-signed packets are simpler */
if (!p->security_state.auth_info || !p->security_state.generic_state) {
@@ -282,30 +288,21 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
/* sign or seal the packet */
switch (p->security_state.auth_info->auth_level) {
case DCERPC_AUTH_LEVEL_PRIVACY:
- status = gensec_seal_packet(p->security_state.generic_state,
- mem_ctx,
- ndr->data + DCERPC_REQUEST_LENGTH,
- ndr->offset - DCERPC_REQUEST_LENGTH,
- &p->security_state.auth_info->credentials);
- break;
-
case DCERPC_AUTH_LEVEL_INTEGRITY:
- status = gensec_sign_packet(p->security_state.generic_state,
- mem_ctx,
- ndr->data + DCERPC_REQUEST_LENGTH,
- ndr->offset - DCERPC_REQUEST_LENGTH,
- &p->security_state.auth_info->credentials);
+ p->security_state.auth_info->credentials
+ = data_blob_talloc(mem_ctx, NULL, gensec_sig_size(p->security_state.generic_state));
+ data_blob_clear(&p->security_state.auth_info->credentials);
break;
case DCERPC_AUTH_LEVEL_NONE:
p->security_state.auth_info->credentials = data_blob(NULL, 0);
break;
-
+
default:
status = NT_STATUS_INVALID_LEVEL;
break;
}
-
+
if (!NT_STATUS_IS_OK(status)) {
return status;
}
@@ -325,6 +322,41 @@ static NTSTATUS dcerpc_push_request_sign(struct dcerpc_pipe *p,
dcerpc_set_frag_length(blob, blob->length);
dcerpc_set_auth_length(blob, p->security_state.auth_info->credentials.length);
+ /* sign or seal the packet */
+ switch (p->security_state.auth_info->auth_level) {
+ case DCERPC_AUTH_LEVEL_PRIVACY:
+ status = gensec_seal_packet(p->security_state.generic_state,
+ mem_ctx,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ blob->data,
+ blob->length -
+ p->security_state.auth_info->credentials.length,
+ &creds2);
+ memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_INTEGRITY:
+ status = gensec_sign_packet(p->security_state.generic_state,
+ mem_ctx,
+ ndr->data + DCERPC_REQUEST_LENGTH,
+ ndr->offset - DCERPC_REQUEST_LENGTH,
+ blob->data,
+ blob->length -
+ p->security_state.auth_info->credentials.length,
+ &creds2);
+ memcpy(blob->data + blob->length - creds2.length, creds2.data, creds2.length);
+ break;
+
+ case DCERPC_AUTH_LEVEL_NONE:
+ p->security_state.auth_info->credentials = data_blob(NULL, 0);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+
data_blob_free(&p->security_state.auth_info->credentials);
return NT_STATUS_OK;
@@ -433,8 +465,8 @@ NTSTATUS dcerpc_bind(struct dcerpc_pipe *p,
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.max_xmit_frag = 5840;
+ pkt.u.bind.max_recv_frag = 5840;
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]));
@@ -782,60 +814,44 @@ struct rpc_request *dcerpc_request_send(struct dcerpc_pipe *p,
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) {
+ DLIST_ADD(p->pending, req);
+
+ /* we send a series of pdus without waiting for a reply */
+ while (remaining > 0) {
+ uint32_t chunk = MIN(chunk_size, remaining);
+ BOOL last_frag = False;
+
+ pkt.pfc_flags = 0;
+
if (remaining == stub_data->length) {
- pkt.pfc_flags = DCERPC_PFC_FLAG_FIRST;
- } else {
- pkt.pfc_flags = 0;
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_FIRST;
+ }
+ if (chunk == remaining) {
+ pkt.pfc_flags |= DCERPC_PFC_FLAG_LAST;
+ last_frag = True;
}
pkt.u.request.stub_and_verifier.data = stub_data->data +
(stub_data->length - remaining);
- pkt.u.request.stub_and_verifier.length = chunk_size;
+ pkt.u.request.stub_and_verifier.length = chunk;
req->status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->pending, req);
return req;
}
- req->status = p->transport.send_request(p, &blob, False);
+ req->status = p->transport.send_request(p, &blob, last_frag);
if (!NT_STATUS_IS_OK(req->status)) {
req->state = RPC_REQUEST_DONE;
+ DLIST_REMOVE(p->pending, req);
return req;
}
- remaining -= chunk_size;
- }
-
- /* now we send a pdu with LAST_FRAG sent and get the first
- part of the reply */
- if (remaining == stub_data->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->data +
- (stub_data->length - remaining);
- pkt.u.request.stub_and_verifier.length = remaining;
-
- req->status = dcerpc_push_request_sign(p, &blob, mem_ctx, &pkt);
- if (!NT_STATUS_IS_OK(req->status)) {
- req->state = RPC_REQUEST_DONE;
- return req;
- }
-
- /* send the final pdu */
- req->status = p->transport.send_request(p, &blob, True);
-
- if (!NT_STATUS_IS_OK(req->status)) {
- req->state = RPC_REQUEST_DONE;
+ remaining -= chunk;
}
- DLIST_ADD(p->pending, req);
-
return req;
}
diff --git a/source4/librpc/rpc/dcerpc.h b/source4/librpc/rpc/dcerpc.h
index 16bf52cec2..242a18368f 100644
--- a/source4/librpc/rpc/dcerpc.h
+++ b/source4/librpc/rpc/dcerpc.h
@@ -29,6 +29,9 @@ struct dcerpc_pipe;
struct dcerpc_security {
struct dcerpc_auth *auth_info;
struct gensec_security *generic_state;
+
+ /* get the session key */
+ NTSTATUS (*session_key)(struct dcerpc_pipe *, DATA_BLOB *);
};
struct dcerpc_pipe {
@@ -64,8 +67,6 @@ struct dcerpc_pipe {
has been received */
void (*recv_data)(struct dcerpc_pipe *, DATA_BLOB *, NTSTATUS status);
- /* get the transport level session key */
- NTSTATUS (*session_key)(struct dcerpc_pipe *, DATA_BLOB *);
} transport;
/* the last fault code from a DCERPC fault */
diff --git a/source4/librpc/rpc/dcerpc_auth.c b/source4/librpc/rpc/dcerpc_auth.c
index 9587fb9390..0966b70338 100644
--- a/source4/librpc/rpc/dcerpc_auth.c
+++ b/source4/librpc/rpc/dcerpc_auth.c
@@ -107,13 +107,16 @@ NTSTATUS dcerpc_bind_auth3(struct dcerpc_pipe *p, uint8_t auth_type, uint8_t aut
}
p->security_state.auth_info->credentials = credentials;
-
+
status = dcerpc_auth3(p, mem_ctx);
done:
talloc_destroy(mem_ctx);
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCT(p->security_state);
+ } else {
+ /* Authenticated connections use the generic session key */
+ p->security_state.session_key = dcerpc_generic_session_key;
}
return status;
@@ -196,6 +199,9 @@ done:
if (!NT_STATUS_IS_OK(status)) {
ZERO_STRUCT(p->security_state);
+ } else {
+ /* Authenticated connections use the generic session key */
+ p->security_state.session_key = dcerpc_generic_session_key;
}
return status;
diff --git a/source4/librpc/rpc/dcerpc_schannel.c b/source4/librpc/rpc/dcerpc_schannel.c
index efe609f9a3..9aa2b0c88d 100644
--- a/source4/librpc/rpc/dcerpc_schannel.c
+++ b/source4/librpc/rpc/dcerpc_schannel.c
@@ -50,7 +50,9 @@ static NTSTATUS dcerpc_schannel_key(struct dcerpc_pipe *p,
*/
static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
- uint8_t *data, size_t length, DATA_BLOB *sig)
+ uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
+ DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
@@ -60,6 +62,7 @@ static NTSTATUS dcerpc_schannel_unseal_packet(struct gensec_security *gensec_sec
static NTSTATUS dcerpc_schannel_check_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
const DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
@@ -70,6 +73,7 @@ static NTSTATUS dcerpc_schannel_check_packet(struct gensec_security *gensec_secu
static NTSTATUS dcerpc_schannel_seal_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
@@ -80,6 +84,7 @@ static NTSTATUS dcerpc_schannel_seal_packet(struct gensec_security *gensec_secur
static NTSTATUS dcerpc_schannel_sign_packet(struct gensec_security *gensec_security,
TALLOC_CTX *mem_ctx,
const uint8_t *data, size_t length,
+ const uint8_t *whole_pdu, size_t pdu_length,
DATA_BLOB *sig)
{
struct dcerpc_schannel_state *dce_schan_state = gensec_security->private_data;
diff --git a/source4/librpc/rpc/dcerpc_smb.c b/source4/librpc/rpc/dcerpc_smb.c
index fa9101bbd6..eece07c678 100644
--- a/source4/librpc/rpc/dcerpc_smb.c
+++ b/source4/librpc/rpc/dcerpc_smb.c
@@ -359,7 +359,6 @@ NTSTATUS smb_session_key(struct dcerpc_pipe *p, DATA_BLOB *session_key)
*session_key = smb->tree->session->user_session_key;
return NT_STATUS_OK;
}
-
return NT_STATUS_NO_USER_SESSION_KEY;
}
@@ -425,13 +424,15 @@ NTSTATUS dcerpc_pipe_open_smb(struct dcerpc_pipe **p,
(*p)->transport.private = NULL;
(*p)->transport.shutdown_pipe = smb_shutdown_pipe;
(*p)->transport.peer_name = smb_peer_name;
- (*p)->transport.session_key = smb_session_key;
(*p)->transport.send_request = smb_send_request;
(*p)->transport.send_read = send_read_request;
(*p)->transport.event_context = smb_event_context;
(*p)->transport.recv_data = NULL;
+ /* Over-ride the default session key with the SMB session key */
+ (*p)->security_state.session_key = smb_session_key;
+
smb = talloc((*p), sizeof(*smb));
if (!smb) {
dcerpc_pipe_close(*p);
diff --git a/source4/librpc/rpc/dcerpc_tcp.c b/source4/librpc/rpc/dcerpc_tcp.c
index c290891b61..2152c4c5d0 100644
--- a/source4/librpc/rpc/dcerpc_tcp.c
+++ b/source4/librpc/rpc/dcerpc_tcp.c
@@ -87,6 +87,10 @@ static void tcp_process_send(struct dcerpc_pipe *p)
}
break;
}
+ if (ret == 0) {
+ tcp_sock_dead(p, NT_STATUS_NET_WRITE_FAULT);
+ break;
+ }
blob->data.data += ret;
blob->data.length -= ret;
@@ -271,19 +275,6 @@ static const char *tcp_peer_name(struct dcerpc_pipe *p)
return tcp->server_name;
}
-
-/*
- fetch the user session key
-*/
-NTSTATUS tcp_session_key(struct dcerpc_pipe *p, DATA_BLOB *session_key)
-{
- /* this took quite a few CPU cycles to find ... */
- session_key->data = "SystemLibraryDTC";
- session_key->length = 16;
-
- return NT_STATUS_OK;
-}
-
/*
open a rpc connection to a named pipe
*/
@@ -331,7 +322,6 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p,
(*p)->transport.shutdown_pipe = tcp_shutdown_pipe;
(*p)->transport.peer_name = tcp_peer_name;
- (*p)->transport.session_key = tcp_session_key;
tcp = talloc((*p), sizeof(*tcp));
if (!tcp) {
@@ -356,5 +346,8 @@ NTSTATUS dcerpc_pipe_open_tcp(struct dcerpc_pipe **p,
(*p)->transport.private = tcp;
+ /* ensure we don't get SIGPIPE */
+ BlockSignals(True,SIGPIPE);
+
return NT_STATUS_OK;
}
diff --git a/source4/librpc/rpc/dcerpc_util.c b/source4/librpc/rpc/dcerpc_util.c
index fc9f6c847d..1898575003 100644
--- a/source4/librpc/rpc/dcerpc_util.c
+++ b/source4/librpc/rpc/dcerpc_util.c
@@ -458,8 +458,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
pipe_name += 6;
}
- if ((binding->flags & (DCERPC_SCHANNEL_ANY | DCERPC_SIGN | DCERPC_SEAL))
- || !username || !username[0]) {
+ if (!username || !username[0]) {
status = smbcli_full_connection(&cli, lp_netbios_name(),
binding->host, NULL,
"ipc$", "?????",
@@ -500,6 +499,7 @@ static NTSTATUS dcerpc_pipe_connect_ncacn_np(struct dcerpc_pipe **p,
status = dcerpc_bind_auth_ntlm(*p, pipe_uuid, pipe_version, domain, username, password);
} else {
status = dcerpc_bind_auth_none(*p, pipe_uuid, pipe_version);
+
}
if (!NT_STATUS_IS_OK(status)) {
@@ -691,15 +691,22 @@ NTSTATUS dcerpc_secondary_connection(struct dcerpc_pipe *p, struct dcerpc_pipe *
return NT_STATUS_OK;
}
+NTSTATUS dcerpc_generic_session_key(struct dcerpc_pipe *p,
+ DATA_BLOB *session_key)
+{
+ /* this took quite a few CPU cycles to find ... */
+ session_key->data = "SystemLibraryDTC";
+ session_key->length = 16;
+ return NT_STATUS_OK;
+}
/*
- fetch the user session key for the underlying transport. Currently
- only works for the ncacn_np transport
+ fetch the user session key - may be default (above) or the SMB session key
*/
NTSTATUS dcerpc_fetch_session_key(struct dcerpc_pipe *p,
DATA_BLOB *session_key)
{
- return p->transport.session_key(p, session_key);
+ return p->security_state.session_key(p, session_key);
}