diff options
author | Jeremy Allison <jra@samba.org> | 2010-02-17 15:27:59 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2010-02-17 15:27:59 -0800 |
commit | 7b4387f765e34177000c8218f51e2c1d227504e6 (patch) | |
tree | 8a180fa04ecf8c35e2c3977142c37e2173cee431 /source3/rpc_client | |
parent | 5564e7147fdbb136775b990d9a5d37d4d232d936 (diff) | |
download | samba-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.c | 62 |
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. */ |