summaryrefslogtreecommitdiff
path: root/source3/rpc_client
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2010-02-17 15:27:59 -0800
committerJeremy Allison <jra@samba.org>2010-02-17 15:27:59 -0800
commit7b4387f765e34177000c8218f51e2c1d227504e6 (patch)
tree8a180fa04ecf8c35e2c3977142c37e2173cee431 /source3/rpc_client
parent5564e7147fdbb136775b990d9a5d37d4d232d936 (diff)
downloadsamba-7b4387f765e34177000c8218f51e2c1d227504e6.tar.gz
samba-7b4387f765e34177000c8218f51e2c1d227504e6.tar.bz2
samba-7b4387f765e34177000c8218f51e2c1d227504e6.zip
Fix bug #7146 - Samba miss-parses authenticated RPC packets.
Parts of the Samba RPC client and server code misinterpret authenticated packets. DCE authenticated packets actually look like this : +--------------------------+ |header | | ... frag_len (packet len)| | ... auth_len | +--------------------------+ | | | Data payload | ... .... | | +--------------------------+ | | | auth_pad_len bytes | +--------------------------+ | | | Auth footer | | auth_pad_len value | +--------------------------+ | | | Auth payload | | (auth_len bytes long) | +--------------------------+ That's right. The pad bytes come *before* the footer specifying how many pad bytes there are. In order to read this you must seek to the end of the packet and subtract the auth_len (in the packet header) and the auth footer length (a known value). The client and server code gets this right (mostly) in 3.0.x -> 3.4.x so long as the pad alignment is on an 8 byte boundary (there are some special cases in the code for this). Tridge discovered there are some (DRS replication) cases where on 64-bit machines where the pad alignment is on a 16-byte boundary. This breaks the existing S3 hand-optimized rpc code. This patch removes all the special cases in client and server code, and allows the pad alignment for generated packets to be specified by changing a constant in include/local.h (this doesn't affect received packets, the new code always handles them correctly whatever pad alignment is used). This patch also works correctly with rpcclient using sign+seal from the 3.4.x and 3.3.x builds (testing with 3.0.x and 3.2.x to follow) so even as a server it should still work with older libsmbclient and winbindd code. Jeremy
Diffstat (limited to 'source3/rpc_client')
-rw-r--r--source3/rpc_client/cli_pipe.c62
1 files changed, 47 insertions, 15 deletions
diff --git a/source3/rpc_client/cli_pipe.c b/source3/rpc_client/cli_pipe.c
index 48e2f9eb51..2f7db99f20 100644
--- a/source3/rpc_client/cli_pipe.c
+++ b/source3/rpc_client/cli_pipe.c
@@ -650,8 +650,9 @@ static NTSTATUS cli_pipe_verify_ntlmssp(struct rpc_pipe_client *cli, RPC_HDR *pr
}
/* Ensure there's enough data for an authenticated response. */
- if ((auth_len > RPC_MAX_SIGN_SIZE) ||
- (RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len)) {
+ if (auth_len > RPC_MAX_PDU_FRAG_LEN ||
+ prhdr->frag_len < RPC_HEADER_LEN + RPC_HDR_RESP_LEN +
+ RPC_HDR_AUTH_LEN + auth_len) {
DEBUG(0,("cli_pipe_verify_ntlmssp: auth_len %u is too large.\n",
(unsigned int)auth_len ));
return NT_STATUS_BUFFER_TOO_SMALL;
@@ -671,17 +672,31 @@ static NTSTATUS cli_pipe_verify_ntlmssp(struct rpc_pipe_client *cli, RPC_HDR *pr
full_packet_data_len = prhdr->frag_len - auth_len;
/* Pull the auth header and the following data into a blob. */
- if(!prs_set_offset(current_pdu, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len)) {
+ /* NB. The offset of the auth_header is relative to the *end*
+ * of the packet, not the start. */
+ if(!prs_set_offset(current_pdu, prhdr->frag_len - RPC_HDR_AUTH_LEN - auth_len)) {
DEBUG(0,("cli_pipe_verify_ntlmssp: cannot move offset to %u.\n",
(unsigned int)RPC_HEADER_LEN + (unsigned int)RPC_HDR_RESP_LEN + (unsigned int)data_len ));
return NT_STATUS_BUFFER_TOO_SMALL;
- }
+ }
if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, current_pdu, 0)) {
DEBUG(0,("cli_pipe_verify_ntlmssp: failed to unmarshall RPC_HDR_AUTH.\n"));
return NT_STATUS_BUFFER_TOO_SMALL;
}
+ /* Ensure auth_pad_len fits into the packet. */
+ if (RPC_HEADER_LEN + RPC_HDR_REQ_LEN + auth_info.auth_pad_len +
+ RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len) {
+ DEBUG(0,("cli_pipe_verify_ntlmssp: auth_info.auth_pad_len "
+ "too large (%u), auth_len (%u), frag_len = (%u).\n",
+ (unsigned int)auth_info.auth_pad_len,
+ (unsigned int)auth_len,
+ (unsigned int)prhdr->frag_len ));
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
+
auth_blob.data = (unsigned char *)prs_data_p(current_pdu) + prs_offset(current_pdu);
auth_blob.length = auth_len;
@@ -775,7 +790,7 @@ static NTSTATUS cli_pipe_verify_schannel(struct rpc_pipe_client *cli, RPC_HDR *p
}
/* Ensure there's enough data for an authenticated response. */
- if ((auth_len > RPC_MAX_SIGN_SIZE) ||
+ if ((auth_len > RPC_MAX_PDU_FRAG_LEN) ||
(RPC_HEADER_LEN + RPC_HDR_RESP_LEN + RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len)) {
DEBUG(0,("cli_pipe_verify_schannel: auth_len %u is too large.\n",
(unsigned int)auth_len ));
@@ -784,9 +799,15 @@ static NTSTATUS cli_pipe_verify_schannel(struct rpc_pipe_client *cli, RPC_HDR *p
data_len = prhdr->frag_len - RPC_HEADER_LEN - RPC_HDR_RESP_LEN - RPC_HDR_AUTH_LEN - auth_len;
- if(!prs_set_offset(current_pdu, RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len)) {
- DEBUG(0,("cli_pipe_verify_schannel: cannot move offset to %u.\n",
- (unsigned int)RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len ));
+ /* Pull the auth header and the following data into a blob. */
+ /* NB. The offset of the auth_header is relative to the *end*
+ * of the packet, not the start. */
+ if(!prs_set_offset(current_pdu,
+ prhdr->frag_len - RPC_HDR_AUTH_LEN - auth_len)) {
+ DEBUG(0,("cli_pipe_verify_schannel: cannot move "
+ "offset to %u.\n",
+ (unsigned int)(prhdr->frag_len -
+ RPC_HDR_AUTH_LEN - auth_len) ));
return NT_STATUS_BUFFER_TOO_SMALL;
}
@@ -795,6 +816,17 @@ static NTSTATUS cli_pipe_verify_schannel(struct rpc_pipe_client *cli, RPC_HDR *p
return NT_STATUS_BUFFER_TOO_SMALL;
}
+ /* Ensure auth_pad_len fits into the packet. */
+ if (RPC_HEADER_LEN + RPC_HDR_REQ_LEN + auth_info.auth_pad_len +
+ RPC_HDR_AUTH_LEN + auth_len > prhdr->frag_len) {
+ DEBUG(0,("cli_pipe_verify_schannel: auth_info.auth_pad_len "
+ "too large (%u), auth_len (%u), frag_len = (%u).\n",
+ (unsigned int)auth_info.auth_pad_len,
+ (unsigned int)auth_len,
+ (unsigned int)prhdr->frag_len ));
+ return NT_STATUS_BUFFER_TOO_SMALL;
+ }
+
if (auth_info.auth_type != DCERPC_AUTH_TYPE_SCHANNEL) {
DEBUG(0,("cli_pipe_verify_schannel: Invalid auth info %d on schannel\n",
auth_info.auth_type));
@@ -1790,8 +1822,8 @@ static NTSTATUS create_bind_or_alt_ctx_internal(enum dcerpc_pkt_type pkt_type,
/* Do we need to pad ? */
if (auth_len) {
uint16 data_len = RPC_HEADER_LEN + RPC_HDR_RB_LEN(&hdr_rb);
- if (data_len % 8) {
- ss_padding_len = 8 - (data_len % 8);
+ if (data_len % CLIENT_NDR_PADDING_SIZE) {
+ ss_padding_len = CLIENT_NDR_PADDING_SIZE - (data_len % CLIENT_NDR_PADDING_SIZE);
phdr_auth->auth_pad_len = ss_padding_len;
}
frag_len += RPC_HDR_AUTH_LEN + auth_len + ss_padding_len;
@@ -1818,8 +1850,8 @@ static NTSTATUS create_bind_or_alt_ctx_internal(enum dcerpc_pkt_type pkt_type,
if(auth_len != 0) {
if (ss_padding_len) {
- char pad[8];
- memset(pad, '\0', 8);
+ char pad[CLIENT_NDR_PADDING_SIZE];
+ memset(pad, '\0', CLIENT_NDR_PADDING_SIZE);
if (!prs_copy_data_in(rpc_out, pad, ss_padding_len)) {
DEBUG(0,("create_bind_or_alt_ctx_internal: failed to marshall padding.\n"));
return NT_STATUS_NO_MEMORY;
@@ -2120,8 +2152,8 @@ static uint32 calculate_data_len_tosend(struct rpc_pipe_client *cli,
data_len = MIN(data_space, data_left);
*p_ss_padding = 0;
- if (data_len % 8) {
- *p_ss_padding = 8 - (data_len % 8);
+ if (data_len % CLIENT_NDR_PADDING_SIZE) {
+ *p_ss_padding = CLIENT_NDR_PADDING_SIZE - (data_len % CLIENT_NDR_PADDING_SIZE);
}
*p_frag_len = RPC_HEADER_LEN + RPC_HDR_REQ_LEN + /* Normal headers. */
data_len + *p_ss_padding + /* data plus padding. */
@@ -2517,7 +2549,7 @@ static NTSTATUS create_rpc_bind_auth3(struct rpc_pipe_client *cli,
/*
I'm puzzled about this - seems to violate the DCE RPC auth rules,
- about padding - shouldn't this pad to length 8 ? JRA.
+ about padding - shouldn't this pad to length CLIENT_NDR_PADDING_SIZE ? JRA.
*/
/* 4 bytes padding. */