From 7b4387f765e34177000c8218f51e2c1d227504e6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 17 Feb 2010 15:27:59 -0800 Subject: 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 --- source3/rpc_client/cli_pipe.c | 62 ++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) (limited to 'source3/rpc_client/cli_pipe.c') 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. */ -- cgit