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/include/local.h | 3 + source3/rpc_client/cli_pipe.c | 62 ++++-- source3/rpc_parse/parse_rpc.c | 5 +- source3/rpc_server/srv_pipe.c | 431 ++++++++++++++++++++++++++++++------------ 4 files changed, 363 insertions(+), 138 deletions(-) diff --git a/source3/include/local.h b/source3/include/local.h index a88b17be13..a3baf64f9a 100644 --- a/source3/include/local.h +++ b/source3/include/local.h @@ -274,4 +274,7 @@ /* Maximum size of RPC data we will accept for one call. */ #define MAX_RPC_DATA_SIZE (15*1024*1024) +#define CLIENT_NDR_PADDING_SIZE 8 +#define SERVER_NDR_PADDING_SIZE 8 + #endif 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. */ diff --git a/source3/rpc_parse/parse_rpc.c b/source3/rpc_parse/parse_rpc.c index f720de35a1..441a00b1ea 100644 --- a/source3/rpc_parse/parse_rpc.c +++ b/source3/rpc_parse/parse_rpc.c @@ -480,6 +480,8 @@ void init_rpc_hdr_auth(RPC_HDR_AUTH *rai, /******************************************************************* Reads or writes an RPC_HDR_AUTH structure. + NB This writes UNALIGNED. Ensure you're correctly aligned before + calling. ********************************************************************/ bool smb_io_rpc_hdr_auth(const char *desc, RPC_HDR_AUTH *rai, prs_struct *ps, int depth) @@ -490,9 +492,6 @@ bool smb_io_rpc_hdr_auth(const char *desc, RPC_HDR_AUTH *rai, prs_struct *ps, in prs_debug(ps, depth, desc, "smb_io_rpc_hdr_auth"); depth++; - if(!prs_align(ps)) - return False; - if(!prs_uint8 ("auth_type ", ps, depth, &rai->auth_type)) return False; if(!prs_uint8 ("auth_level ", ps, depth, &rai->auth_level)) diff --git a/source3/rpc_server/srv_pipe.c b/source3/rpc_server/srv_pipe.c index f92a100d0a..6b08f1f9b3 100644 --- a/source3/rpc_server/srv_pipe.c +++ b/source3/rpc_server/srv_pipe.c @@ -131,11 +131,12 @@ static bool create_next_pdu_ntlmssp(pipes_struct *p) if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) { p->hdr.flags |= DCERPC_PFC_FLAG_LAST; - if (data_len_left % 8) { - ss_padding_len = 8 - (data_len_left % 8); - DEBUG(10,("create_next_pdu_ntlmssp: adding sign/seal padding of %u\n", - ss_padding_len )); - } + } + + if (data_len_left % SERVER_NDR_PADDING_SIZE) { + ss_padding_len = SERVER_NDR_PADDING_SIZE - (data_len_left % SERVER_NDR_PADDING_SIZE); + DEBUG(10,("create_next_pdu_ntlmssp: adding sign/seal padding of %u\n", + ss_padding_len )); } /* @@ -179,9 +180,9 @@ static bool create_next_pdu_ntlmssp(pipes_struct *p) /* Copy the sign/seal padding data. */ if (ss_padding_len) { - char pad[8]; + char pad[SERVER_NDR_PADDING_SIZE]; - memset(pad, '\0', 8); + memset(pad, '\0', SERVER_NDR_PADDING_SIZE); if (!prs_copy_data_in(&p->out_data.frag, pad, ss_padding_len)) { DEBUG(0,("create_next_pdu_ntlmssp: failed to add %u bytes of pad data.\n", @@ -205,8 +206,9 @@ static bool create_next_pdu_ntlmssp(pipes_struct *p) } init_rpc_hdr_auth(&auth_info, auth_type, auth_level, ss_padding_len, 1 /* context id. */); - if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &p->out_data.frag, - 0)) { + + if (!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, + &p->out_data.frag, 0)) { DEBUG(0,("create_next_pdu_ntlmssp: failed to marshall RPC_HDR_AUTH.\n")); prs_mem_free(&p->out_data.frag); return False; @@ -350,11 +352,11 @@ static bool create_next_pdu_schannel(pipes_struct *p) if(p->out_data.data_sent_length + data_len >= prs_offset(&p->out_data.rdata)) { p->hdr.flags |= DCERPC_PFC_FLAG_LAST; - if (data_len_left % 8) { - ss_padding_len = 8 - (data_len_left % 8); - DEBUG(10,("create_next_pdu_schannel: adding sign/seal padding of %u\n", - ss_padding_len )); - } + } + if (data_len_left % SERVER_NDR_PADDING_SIZE) { + ss_padding_len = SERVER_NDR_PADDING_SIZE - (data_len_left % SERVER_NDR_PADDING_SIZE); + DEBUG(10,("create_next_pdu_schannel: adding sign/seal padding of %u\n", + ss_padding_len )); } p->hdr.frag_len = RPC_HEADER_LEN + RPC_HDR_RESP_LEN + data_len + ss_padding_len + @@ -395,8 +397,8 @@ static bool create_next_pdu_schannel(pipes_struct *p) /* Copy the sign/seal padding data. */ if (ss_padding_len) { - char pad[8]; - memset(pad, '\0', 8); + char pad[SERVER_NDR_PADDING_SIZE]; + memset(pad, '\0', SERVER_NDR_PADDING_SIZE); if (!prs_copy_data_in(&p->out_data.frag, pad, ss_padding_len)) { DEBUG(0,("create_next_pdu_schannel: failed to add %u bytes of pad data.\n", (unsigned int)ss_padding_len)); @@ -421,7 +423,7 @@ static bool create_next_pdu_schannel(pipes_struct *p) DCERPC_AUTH_LEVEL_PRIVACY : DCERPC_AUTH_LEVEL_INTEGRITY, ss_padding_len, 1); - if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, + if (!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, &p->out_data.frag, 0)) { DEBUG(0,("create_next_pdu_schannel: failed to marshall RPC_HDR_AUTH.\n")); prs_mem_free(&p->out_data.frag); @@ -746,12 +748,13 @@ bool api_pipe_bind_auth3(pipes_struct *p, prs_struct *rpc_in_p) RPC_HDR_AUTH auth_info; uint32 pad = 0; DATA_BLOB blob; + uint32_t auth_len = p->hdr.auth_len; ZERO_STRUCT(blob); DEBUG(5,("api_pipe_bind_auth3: decode request. %d\n", __LINE__)); - if (p->hdr.auth_len == 0) { + if (auth_len == 0) { DEBUG(0,("api_pipe_bind_auth3: No auth field sent !\n")); goto err; } @@ -762,15 +765,45 @@ bool api_pipe_bind_auth3(pipes_struct *p, prs_struct *rpc_in_p) goto err; } + /* Ensure there's enough data for an authenticated request. */ + if (RPC_HEADER_LEN + RPC_HDR_AUTH_LEN + auth_len > + p->hdr.frag_len) { + DEBUG(0,("api_pipe_ntlmssp_auth_process: auth_len " + "%u is too large.\n", + (unsigned int)auth_len )); + goto err; + } + /* * Decode the authentication verifier response. */ - if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) { - DEBUG(0,("api_pipe_bind_auth3: unmarshall of RPC_HDR_AUTH failed.\n")); + /* 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. Also, the length of the + * data in rpc_in_p is p->hdr.frag_len - RPC_HEADER_LEN, + * as the RPC header isn't included in rpc_in_p. */ + if(!prs_set_offset(rpc_in_p, + p->hdr.frag_len - RPC_HEADER_LEN - + RPC_HDR_AUTH_LEN - auth_len)) { + DEBUG(0,("api_pipe_bind_auth3: cannot move " + "offset to %u.\n", + (unsigned int)(p->hdr.frag_len - + RPC_HDR_AUTH_LEN - auth_len) )); + goto err; + } + + if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_bind_auth3: failed to " + "unmarshall RPC_HDR_AUTH.\n")); goto err; } + /* We must NEVER look at auth_info->auth_pad_len here, + * as old Samba client code gets it wrong and sends it + * as zero. JRA. + */ + if (auth_info.auth_type != DCERPC_AUTH_TYPE_NTLMSSP) { DEBUG(0,("api_pipe_bind_auth3: incorrect auth type (%u).\n", (unsigned int)auth_info.auth_type )); @@ -1154,6 +1187,7 @@ static bool pipe_spnego_auth_bind_kerberos(pipes_struct *p, prs_struct *rpc_in_p *******************************************************************/ static bool pipe_spnego_auth_bind_negotiate(pipes_struct *p, prs_struct *rpc_in_p, + uint32_t ss_padding_len, RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth) { DATA_BLOB blob; @@ -1245,8 +1279,11 @@ static bool pipe_spnego_auth_bind_negotiate(pipes_struct *p, prs_struct *rpc_in_ OID_NTLMSSP); } + /* auth_pad_len will be handled by the caller */ + /* Copy the blob into the pout_auth parse struct */ - init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SPNEGO, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1); + init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SPNEGO, + pauth_info->auth_level, ss_padding_len, 1); if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) { DEBUG(0,("pipe_spnego_auth_bind_negotiate: marshalling of RPC_HDR_AUTH failed.\n")); goto err; @@ -1286,7 +1323,8 @@ static bool pipe_spnego_auth_bind_negotiate(pipes_struct *p, prs_struct *rpc_in_ *******************************************************************/ static bool pipe_spnego_auth_bind_continue(pipes_struct *p, prs_struct *rpc_in_p, - RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth) + uint32_t ss_padding_len, + RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth) { RPC_HDR_AUTH auth_info; DATA_BLOB spnego_blob; @@ -1343,8 +1381,11 @@ static bool pipe_spnego_auth_bind_continue(pipes_struct *p, prs_struct *rpc_in_p /* Generate the spnego "accept completed" blob - no incoming data. */ response = spnego_gen_auth_response(&auth_reply, NT_STATUS_OK, OID_NTLMSSP); + /* FIXME - add auth_pad_len here ! */ + /* Copy the blob into the pout_auth parse struct */ - init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SPNEGO, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1); + init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SPNEGO, + pauth_info->auth_level, ss_padding_len, 1); if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) { DEBUG(0,("pipe_spnego_auth_bind_continue: marshalling of RPC_HDR_AUTH failed.\n")); goto err; @@ -1380,6 +1421,7 @@ static bool pipe_spnego_auth_bind_continue(pipes_struct *p, prs_struct *rpc_in_p *******************************************************************/ static bool pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, + uint32_t ss_padding_len, RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth) { RPC_HDR_AUTH auth_info; @@ -1466,7 +1508,8 @@ static bool pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, return false; } - init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SCHANNEL, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1); + init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_SCHANNEL, + pauth_info->auth_level, ss_padding_len, 1); if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) { DEBUG(0,("pipe_schannel_auth_bind: marshalling of RPC_HDR_AUTH failed.\n")); return False; @@ -1512,6 +1555,7 @@ static bool pipe_schannel_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, *******************************************************************/ static bool pipe_ntlmssp_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, + uint32_t ss_padding_len, RPC_HDR_AUTH *pauth_info, prs_struct *pout_auth) { RPC_HDR_AUTH auth_info; @@ -1555,7 +1599,8 @@ static bool pipe_ntlmssp_auth_bind(pipes_struct *p, prs_struct *rpc_in_p, data_blob_free(&blob); /* Copy the blob into the pout_auth parse struct */ - init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_NTLMSSP, pauth_info->auth_level, RPC_HDR_AUTH_LEN, 1); + init_rpc_hdr_auth(&auth_info, DCERPC_AUTH_TYPE_NTLMSSP, + pauth_info->auth_level, ss_padding_len, 1); if(!smb_io_rpc_hdr_auth("", &auth_info, pout_auth, 0)) { DEBUG(0,("pipe_ntlmssp_auth_bind: marshalling of RPC_HDR_AUTH failed.\n")); goto err; @@ -1604,6 +1649,7 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) int i = 0; int auth_len = 0; unsigned int auth_type = DCERPC_AUTH_TYPE_NONE; + uint32_t ss_padding_len = 0; /* No rebinds on a bound pipe - use alter context. */ if (p->pipe_bound) { @@ -1717,6 +1763,45 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) DEBUG(5,("api_pipe_bind_req: make response. %d\n", __LINE__)); + assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0; + + /* + * Create the bind response struct. + */ + + /* If the requested abstract synt uuid doesn't match our client pipe, + reject the bind_ack & set the transfer interface synt to all 0's, + ver 0 (observed when NT5 attempts to bind to abstract interfaces + unknown to NT4) + Needed when adding entries to a DACL from NT5 - SK */ + + if(check_bind_req(p, &hdr_rb.rpc_context[0].abstract, &hdr_rb.rpc_context[0].transfer[0], + hdr_rb.rpc_context[0].context_id )) { + init_rpc_hdr_ba(&hdr_ba, + RPC_MAX_PDU_FRAG_LEN, + RPC_MAX_PDU_FRAG_LEN, + assoc_gid, + ack_pipe_name, + 0x1, 0x0, 0x0, + &hdr_rb.rpc_context[0].transfer[0]); + } else { + /* Rejection reason: abstract syntax not supported */ + init_rpc_hdr_ba(&hdr_ba, RPC_MAX_PDU_FRAG_LEN, + RPC_MAX_PDU_FRAG_LEN, assoc_gid, + ack_pipe_name, 0x1, 0x2, 0x1, + &null_ndr_syntax_id); + p->pipe_bound = False; + } + + /* + * and marshall it. + */ + + if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) { + DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n")); + goto err_exit; + } + /* * Check if this is an authenticated bind request. */ @@ -1726,6 +1811,40 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) * Decode the authentication verifier. */ + /* Work out any padding needed before the auth footer. */ + if ((RPC_HEADER_LEN + prs_offset(&out_hdr_ba)) % SERVER_NDR_PADDING_SIZE) { + ss_padding_len = SERVER_NDR_PADDING_SIZE - + ((RPC_HEADER_LEN + prs_offset(&out_hdr_ba)) % SERVER_NDR_PADDING_SIZE); + DEBUG(10,("api_pipe_bind_req: auth pad_len = %u\n", + (unsigned int)ss_padding_len )); + } + + /* Quick length check. Won't catch a bad auth footer, + * prevents overrun. */ + + if (p->hdr.frag_len < RPC_HEADER_LEN + RPC_HDR_AUTH_LEN + p->hdr.auth_len) { + DEBUG(0,("api_pipe_bind_req: auth_len (%u) " + "too long for fragment %u.\n", + (unsigned int)p->hdr.auth_len, + (unsigned int)p->hdr.frag_len )); + goto err_exit; + } + + /* 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. Also, the length of the + * data in rpc_in_p is p->hdr.frag_len - RPC_HEADER_LEN, + * as the RPC header isn't included in rpc_in_p. */ + if(!prs_set_offset(rpc_in_p, + p->hdr.frag_len - RPC_HEADER_LEN - + RPC_HDR_AUTH_LEN - p->hdr.auth_len)) { + DEBUG(0,("api_pipe_bind_req: cannot move " + "offset to %u.\n", + (unsigned int)(p->hdr.frag_len - + RPC_HDR_AUTH_LEN - p->hdr.auth_len) )); + goto err_exit; + } + if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) { DEBUG(0,("api_pipe_bind_req: unable to unmarshall RPC_HDR_AUTH struct.\n")); goto err_exit; @@ -1750,24 +1869,25 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) ZERO_STRUCT(auth_info); } - assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0; - switch(auth_type) { case DCERPC_AUTH_TYPE_NTLMSSP: - if (!pipe_ntlmssp_auth_bind(p, rpc_in_p, &auth_info, &out_auth)) { + if (!pipe_ntlmssp_auth_bind(p, rpc_in_p, + ss_padding_len, &auth_info, &out_auth)) { goto err_exit; } assoc_gid = 0x7a77; break; case DCERPC_AUTH_TYPE_SCHANNEL: - if (!pipe_schannel_auth_bind(p, rpc_in_p, &auth_info, &out_auth)) { + if (!pipe_schannel_auth_bind(p, rpc_in_p, + ss_padding_len, &auth_info, &out_auth)) { goto err_exit; } break; case DCERPC_AUTH_TYPE_SPNEGO: - if (!pipe_spnego_auth_bind_negotiate(p, rpc_in_p, &auth_info, &out_auth)) { + if (!pipe_spnego_auth_bind_negotiate(p, rpc_in_p, + ss_padding_len, &auth_info, &out_auth)) { goto err_exit; } break; @@ -1781,50 +1901,13 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) p->pipe_bound = True; /* The session key was initialized from the SMB * session in make_internal_rpc_pipe_p */ + ss_padding_len = 0; break; default: DEBUG(0,("api_pipe_bind_req: unknown auth type %x requested.\n", auth_type )); goto err_exit; } - - /* - * Create the bind response struct. - */ - - /* If the requested abstract synt uuid doesn't match our client pipe, - reject the bind_ack & set the transfer interface synt to all 0's, - ver 0 (observed when NT5 attempts to bind to abstract interfaces - unknown to NT4) - Needed when adding entries to a DACL from NT5 - SK */ - - if(check_bind_req(p, &hdr_rb.rpc_context[0].abstract, &hdr_rb.rpc_context[0].transfer[0], - hdr_rb.rpc_context[0].context_id )) { - init_rpc_hdr_ba(&hdr_ba, - RPC_MAX_PDU_FRAG_LEN, - RPC_MAX_PDU_FRAG_LEN, - assoc_gid, - ack_pipe_name, - 0x1, 0x0, 0x0, - &hdr_rb.rpc_context[0].transfer[0]); - } else { - /* Rejection reason: abstract syntax not supported */ - init_rpc_hdr_ba(&hdr_ba, RPC_MAX_PDU_FRAG_LEN, - RPC_MAX_PDU_FRAG_LEN, assoc_gid, - ack_pipe_name, 0x1, 0x2, 0x1, - &null_ndr_syntax_id); - p->pipe_bound = False; - } - - /* - * and marshall it. - */ - - if(!smb_io_rpc_hdr_ba("", &hdr_ba, &out_hdr_ba, 0)) { - DEBUG(0,("api_pipe_bind_req: marshalling of RPC_HDR_BA failed.\n")); - goto err_exit; - } - /* * Create the header, now we know the length. */ @@ -1835,7 +1918,8 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) init_rpc_hdr(&p->hdr, DCERPC_PKT_BIND_ACK, DCERPC_PFC_FLAG_FIRST | DCERPC_PFC_FLAG_LAST, p->hdr.call_id, - RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + prs_offset(&out_auth), + RPC_HEADER_LEN + prs_offset(&out_hdr_ba) + + ss_padding_len + prs_offset(&out_auth), auth_len); /* @@ -1856,9 +1940,23 @@ bool api_pipe_bind_req(pipes_struct *p, prs_struct *rpc_in_p) goto err_exit; } - if (auth_len && !prs_append_prs_data( &p->out_data.frag, &out_auth)) { - DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n")); - goto err_exit; + if (auth_len) { + if (ss_padding_len) { + char pad[SERVER_NDR_PADDING_SIZE]; + memset(pad, '\0', SERVER_NDR_PADDING_SIZE); + if (!prs_copy_data_in(&p->out_data.frag, pad, + ss_padding_len)) { + DEBUG(0,("api_pipe_bind_req: failed to add %u " + "bytes of pad data.\n", + (unsigned int)ss_padding_len)); + goto err_exit; + } + } + + if (!prs_append_prs_data( &p->out_data.frag, &out_auth)) { + DEBUG(0,("api_pipe_bind_req: append of auth info failed.\n")); + goto err_exit; + } } /* @@ -1896,6 +1994,7 @@ bool api_pipe_alter_context(pipes_struct *p, prs_struct *rpc_in_p) prs_struct out_hdr_ba; prs_struct out_auth; int auth_len = 0; + uint32_t ss_padding_len = 0; prs_init_empty(&p->out_data.frag, p->mem_ctx, MARSHALL); @@ -1941,39 +2040,6 @@ bool api_pipe_alter_context(pipes_struct *p, prs_struct *rpc_in_p) DEBUG(5,("api_pipe_alter_context: make response. %d\n", __LINE__)); - /* - * Check if this is an authenticated alter context request. - */ - - if (p->hdr.auth_len != 0) { - /* - * Decode the authentication verifier. - */ - - if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) { - DEBUG(0,("api_pipe_alter_context: unable to unmarshall RPC_HDR_AUTH struct.\n")); - goto err_exit; - } - - /* - * Currently only the SPNEGO auth type uses the alter ctx - * response in place of the NTLMSSP auth3 type. - */ - - if (auth_info.auth_type == DCERPC_AUTH_TYPE_SPNEGO) { - /* We can only finish if the pipe is unbound. */ - if (!p->pipe_bound) { - if (!pipe_spnego_auth_bind_continue(p, rpc_in_p, &auth_info, &out_auth)) { - goto err_exit; - } - } else { - goto err_exit; - } - } - } else { - ZERO_STRUCT(auth_info); - } - assoc_gid = hdr_rb.bba.assoc_gid ? hdr_rb.bba.assoc_gid : 0x53f0; /* @@ -2013,6 +2079,74 @@ bool api_pipe_alter_context(pipes_struct *p, prs_struct *rpc_in_p) goto err_exit; } + + /* + * Check if this is an authenticated alter context request. + */ + + if (p->hdr.auth_len != 0) { + /* + * Decode the authentication verifier. + */ + + /* Work out any padding needed before the auth footer. */ + if ((RPC_HEADER_LEN + prs_offset(&out_hdr_ba)) % SERVER_NDR_PADDING_SIZE) { + ss_padding_len = SERVER_NDR_PADDING_SIZE - + ((RPC_HEADER_LEN + prs_offset(&out_hdr_ba)) % SERVER_NDR_PADDING_SIZE); + DEBUG(10,("api_pipe_alter_context: auth pad_len = %u\n", + (unsigned int)ss_padding_len )); + } + + /* Quick length check. Won't catch a bad auth footer, + * prevents overrun. */ + + if (p->hdr.frag_len < RPC_HEADER_LEN + RPC_HDR_AUTH_LEN + p->hdr.auth_len) { + DEBUG(0,("api_pipe_alter_context: auth_len (%u) " + "too long for fragment %u.\n", + (unsigned int)p->hdr.auth_len, + (unsigned int)p->hdr.frag_len )); + goto err_exit; + } + + /* 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. Also, the length of the + * data in rpc_in_p is p->hdr.frag_len - RPC_HEADER_LEN, + * as the RPC header isn't included in rpc_in_p. */ + if(!prs_set_offset(rpc_in_p, + p->hdr.frag_len - RPC_HEADER_LEN - + RPC_HDR_AUTH_LEN - p->hdr.auth_len)) { + DEBUG(0,("api_alter_context: cannot move " + "offset to %u.\n", + (unsigned int)(p->hdr.frag_len - + RPC_HDR_AUTH_LEN - p->hdr.auth_len) )); + goto err_exit; + } + + if(!smb_io_rpc_hdr_auth("", &auth_info, rpc_in_p, 0)) { + DEBUG(0,("api_pipe_alter_context: unable to unmarshall RPC_HDR_AUTH struct.\n")); + goto err_exit; + } + + /* + * Currently only the SPNEGO auth type uses the alter ctx + * response in place of the NTLMSSP auth3 type. + */ + + if (auth_info.auth_type == DCERPC_AUTH_TYPE_SPNEGO) { + /* We can only finish if the pipe is unbound. */ + if (!p->pipe_bound) { + if (!pipe_spnego_auth_bind_continue(p, rpc_in_p, + ss_padding_len, &auth_info, &out_auth)) { + goto err_exit; + } + } else { + goto err_exit; + } + } + } else { + ZERO_STRUCT(auth_info); + } /* * Create the header, now we know the length. */ @@ -2044,9 +2178,23 @@ bool api_pipe_alter_context(pipes_struct *p, prs_struct *rpc_in_p) goto err_exit; } - if (auth_len && !prs_append_prs_data(&p->out_data.frag, &out_auth)) { - DEBUG(0,("api_pipe_alter_context: append of auth info failed.\n")); - goto err_exit; + if (auth_len) { + if (ss_padding_len) { + char pad[SERVER_NDR_PADDING_SIZE]; + memset(pad, '\0', SERVER_NDR_PADDING_SIZE); + if (!prs_copy_data_in(&p->out_data.frag, pad, + ss_padding_len)) { + DEBUG(0,("api_pipe_alter_context: failed to add %u " + "bytes of pad data.\n", + (unsigned int)ss_padding_len)); + goto err_exit; + } + } + + if (!prs_append_prs_data( &p->out_data.frag, &out_auth)) { + DEBUG(0,("api_pipe_alter_context: append of auth info failed.\n")); + goto err_exit; + } } /* @@ -2098,8 +2246,8 @@ bool api_pipe_ntlmssp_auth_process(pipes_struct *p, prs_struct *rpc_in, } /* Ensure there's enough data for an authenticated request. */ - if ((auth_len > RPC_MAX_SIGN_SIZE) || - (RPC_HEADER_LEN + RPC_HDR_REQ_LEN + RPC_HDR_AUTH_LEN + auth_len > p->hdr.frag_len)) { + if (RPC_HEADER_LEN + RPC_HDR_REQ_LEN + RPC_HDR_AUTH_LEN + + auth_len > p->hdr.frag_len) { DEBUG(0,("api_pipe_ntlmssp_auth_process: auth_len %u is too large.\n", (unsigned int)auth_len )); *pstatus = NT_STATUS_INVALID_PARAMETER; @@ -2108,9 +2256,10 @@ bool api_pipe_ntlmssp_auth_process(pipes_struct *p, prs_struct *rpc_in, /* * We need the full packet data + length (minus auth stuff) as well as the packet data + length - * after the RPC header. + * after the RPC header. * We need to pass in the full packet (minus auth len) to the NTLMSSP sign and check seal * functions as NTLMv2 checks the rpc headers also. + * Both of these values include any auth_pad_len bytes. */ data = (unsigned char *)(prs_data_p(rpc_in) + RPC_HDR_REQ_LEN); @@ -2120,15 +2269,36 @@ bool api_pipe_ntlmssp_auth_process(pipes_struct *p, prs_struct *rpc_in, full_packet_data_len = p->hdr.frag_len - auth_len; /* Pull the auth header and the following data into a blob. */ - if(!prs_set_offset(rpc_in, RPC_HDR_REQ_LEN + data_len)) { - DEBUG(0,("api_pipe_ntlmssp_auth_process: cannot move offset to %u.\n", - (unsigned int)RPC_HDR_REQ_LEN + (unsigned int)data_len )); + /* NB. The offset of the auth_header is relative to the *end* + * of the packet, not the start. Also, the length of the + * data in rpc_in_p is p->hdr.frag_len - RPC_HEADER_LEN, + * as the RPC header isn't included in rpc_in_p. */ + if(!prs_set_offset(rpc_in, + p->hdr.frag_len - RPC_HEADER_LEN - + RPC_HDR_AUTH_LEN - auth_len)) { + DEBUG(0,("api_pipe_ntlmssp_auth_process: cannot move " + "offset to %u.\n", + (unsigned int)(p->hdr.frag_len - + RPC_HDR_AUTH_LEN - auth_len) )); *pstatus = NT_STATUS_INVALID_PARAMETER; return False; } if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) { - DEBUG(0,("api_pipe_ntlmssp_auth_process: failed to unmarshall RPC_HDR_AUTH.\n")); + DEBUG(0,("api_pipe_ntlmssp_auth_process: failed to " + "unmarshall RPC_HDR_AUTH.\n")); + *pstatus = NT_STATUS_INVALID_PARAMETER; + return False; + } + + /* 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 > p->hdr.frag_len) { + DEBUG(0,("api_pipe_ntlmssp_auth_process: 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)p->hdr.frag_len )); *pstatus = NT_STATUS_INVALID_PARAMETER; return False; } @@ -2213,7 +2383,7 @@ bool api_pipe_schannel_process(pipes_struct *p, prs_struct *rpc_in, uint32 *p_ss /* * The following is that length of the data we must verify or unseal. * This doesn't include the RPC headers or the auth_len or the RPC_HDR_AUTH_LEN - * preceeding the auth_data. + * preceeding the auth_data, but does include the auth_pad_len bytes. */ if (p->hdr.frag_len < RPC_HEADER_LEN + RPC_HDR_REQ_LEN + RPC_HDR_AUTH_LEN + auth_len) { @@ -2228,14 +2398,35 @@ bool api_pipe_schannel_process(pipes_struct *p, prs_struct *rpc_in, uint32 *p_ss DEBUG(5,("data %d auth %d\n", data_len, auth_len)); - if(!prs_set_offset(rpc_in, RPC_HDR_REQ_LEN + data_len)) { - DEBUG(0,("cannot move offset to %u.\n", - (unsigned int)RPC_HDR_REQ_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. Also, the length of the + * data in rpc_in_p is p->hdr.frag_len - RPC_HEADER_LEN, + * as the RPC header isn't included in rpc_in_p. */ + if(!prs_set_offset(rpc_in, + p->hdr.frag_len - RPC_HEADER_LEN - + RPC_HDR_AUTH_LEN - auth_len)) { + DEBUG(0,("api_pipe_schannel_process: cannot move " + "offset to %u.\n", + (unsigned int)(p->hdr.frag_len - + RPC_HDR_AUTH_LEN - auth_len) )); return False; } if(!smb_io_rpc_hdr_auth("hdr_auth", &auth_info, rpc_in, 0)) { - DEBUG(0,("failed to unmarshall RPC_HDR_AUTH.\n")); + DEBUG(0,("api_pipe_schannel_process: failed to " + "unmarshall RPC_HDR_AUTH.\n")); + return False; + } + + /* 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 > p->hdr.frag_len) { + DEBUG(0,("api_pipe_schannel_process: 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)p->hdr.frag_len )); return False; } -- cgit