2009-04-02 00:37:09 +04:00
/*
Unix SMB / CIFS implementation .
raw dcerpc operations
Copyright ( C ) Andrew Tridgell 2003 - 2005
Copyright ( C ) Jelmer Vernooij 2004 - 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2010-08-12 17:49:32 +04:00
# include "system/network.h"
# include <tevent.h>
# include "lib/tsocket/tsocket.h"
# include "lib/util/tevent_ntstatus.h"
2009-04-02 00:37:09 +04:00
# include "librpc/rpc/dcerpc.h"
# include "librpc/gen_ndr/ndr_dcerpc.h"
2011-02-24 03:55:45 +03:00
# include "rpc_common.h"
2014-01-08 12:50:33 +04:00
# include "lib/util/bitmap.h"
2016-09-06 17:43:53 +03:00
# include "auth/gensec/gensec.h"
2009-04-02 00:37:09 +04:00
/* we need to be able to get/set the fragment length without doing a full
decode */
void dcerpc_set_frag_length ( DATA_BLOB * blob , uint16_t v )
{
if ( CVAL ( blob - > data , DCERPC_DREP_OFFSET ) & DCERPC_DREP_LE ) {
SSVAL ( blob - > data , DCERPC_FRAG_LEN_OFFSET , v ) ;
} else {
RSSVAL ( blob - > data , DCERPC_FRAG_LEN_OFFSET , v ) ;
}
}
uint16_t dcerpc_get_frag_length ( const DATA_BLOB * blob )
{
if ( CVAL ( blob - > data , DCERPC_DREP_OFFSET ) & DCERPC_DREP_LE ) {
return SVAL ( blob - > data , DCERPC_FRAG_LEN_OFFSET ) ;
} else {
return RSVAL ( blob - > data , DCERPC_FRAG_LEN_OFFSET ) ;
}
}
void dcerpc_set_auth_length ( DATA_BLOB * blob , uint16_t v )
{
if ( CVAL ( blob - > data , DCERPC_DREP_OFFSET ) & DCERPC_DREP_LE ) {
SSVAL ( blob - > data , DCERPC_AUTH_LEN_OFFSET , v ) ;
} else {
RSSVAL ( blob - > data , DCERPC_AUTH_LEN_OFFSET , v ) ;
}
}
2010-07-10 02:23:49 +04:00
2010-07-16 23:15:48 +04:00
uint8_t dcerpc_get_endian_flag ( DATA_BLOB * blob )
{
return blob - > data [ DCERPC_DREP_OFFSET ] ;
}
2015-10-28 14:23:00 +03:00
/**
* @ brief Decodes a ncacn_packet
*
* @ param mem_ctx The memory context on which to allocate the packet
* elements
* @ param blob The blob of data to decode
* @ param r An empty ncacn_packet , must not be NULL
*
* @ return a NTSTATUS error code
*/
NTSTATUS dcerpc_pull_ncacn_packet ( TALLOC_CTX * mem_ctx ,
const DATA_BLOB * blob ,
struct ncacn_packet * r )
{
enum ndr_err_code ndr_err ;
struct ndr_pull * ndr ;
ndr = ndr_pull_init_blob ( blob , mem_ctx ) ;
if ( ! ndr ) {
return NT_STATUS_NO_MEMORY ;
}
ndr_err = ndr_pull_ncacn_packet ( ndr , NDR_SCALARS | NDR_BUFFERS , r ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
talloc_free ( ndr ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
talloc_free ( ndr ) ;
if ( r - > frag_length ! = blob - > length ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
return NT_STATUS_OK ;
}
2010-07-20 02:57:12 +04:00
/**
* @ brief Pull a dcerpc_auth structure , taking account of any auth
* padding in the blob . For request / response packets we pass
* the whole data blob , so auth_data_only must be set to false
* as the blob contains data + pad + auth and no just pad + auth .
*
* @ param pkt - The ncacn_packet strcuture
* @ param mem_ctx - The mem_ctx used to allocate dcerpc_auth elements
* @ param pkt_trailer - The packet trailer data , usually the trailing
* auth_info blob , but in the request / response case
* this is the stub_and_verifier blob .
* @ param auth - A preallocated dcerpc_auth * empty * structure
* @ param auth_length - The length of the auth trail , sum of auth header
* lenght and pkt - > auth_length
* @ param auth_data_only - Whether the pkt_trailer includes only the auth_blob
* ( + padding ) or also other data .
*
* @ return - A NTSTATUS error code .
*/
2015-06-28 02:19:57 +03:00
NTSTATUS dcerpc_pull_auth_trailer ( const struct ncacn_packet * pkt ,
2010-07-10 02:23:49 +04:00
TALLOC_CTX * mem_ctx ,
2015-06-28 02:19:57 +03:00
const DATA_BLOB * pkt_trailer ,
2010-07-10 02:23:49 +04:00
struct dcerpc_auth * auth ,
2015-06-28 02:19:57 +03:00
uint32_t * _auth_length ,
2010-07-20 02:57:12 +04:00
bool auth_data_only )
2010-07-10 02:23:49 +04:00
{
struct ndr_pull * ndr ;
enum ndr_err_code ndr_err ;
2015-06-28 02:19:57 +03:00
uint16_t data_and_pad ;
uint16_t auth_length ;
uint32_t tmp_length ;
2016-06-20 17:25:12 +03:00
uint32_t max_pad_len = 0 ;
2015-06-28 02:19:57 +03:00
ZERO_STRUCTP ( auth ) ;
if ( _auth_length ! = NULL ) {
* _auth_length = 0 ;
2016-06-20 17:17:45 +03:00
if ( auth_data_only ) {
return NT_STATUS_INTERNAL_ERROR ;
}
} else {
if ( ! auth_data_only ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2015-06-28 02:19:57 +03:00
}
2015-06-28 02:19:57 +03:00
/* Paranoia checks for auth_length. The caller should check this... */
if ( pkt - > auth_length = = 0 ) {
return NT_STATUS_INTERNAL_ERROR ;
}
2015-06-28 02:19:57 +03:00
/* Paranoia checks for auth_length. The caller should check this... */
if ( pkt - > auth_length > pkt - > frag_length ) {
return NT_STATUS_INTERNAL_ERROR ;
}
tmp_length = DCERPC_NCACN_PAYLOAD_OFFSET ;
tmp_length + = DCERPC_AUTH_TRAILER_LENGTH ;
tmp_length + = pkt - > auth_length ;
if ( tmp_length > pkt - > frag_length ) {
return NT_STATUS_INTERNAL_ERROR ;
}
if ( pkt_trailer - > length > UINT16_MAX ) {
return NT_STATUS_INTERNAL_ERROR ;
}
auth_length = DCERPC_AUTH_TRAILER_LENGTH + pkt - > auth_length ;
if ( pkt_trailer - > length < auth_length ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
2010-07-10 02:23:49 +04:00
}
2015-06-28 02:19:57 +03:00
data_and_pad = pkt_trailer - > length - auth_length ;
2010-07-10 02:23:49 +04:00
2010-07-20 02:57:12 +04:00
ndr = ndr_pull_init_blob ( pkt_trailer , mem_ctx ) ;
2010-07-10 02:23:49 +04:00
if ( ! ndr ) {
return NT_STATUS_NO_MEMORY ;
}
if ( ! ( pkt - > drep [ 0 ] & DCERPC_DREP_LE ) ) {
ndr - > flags | = LIBNDR_FLAG_BIGENDIAN ;
}
2010-07-20 02:57:12 +04:00
ndr_err = ndr_pull_advance ( ndr , data_and_pad ) ;
2010-07-10 02:23:49 +04:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
talloc_free ( ndr ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
ndr_err = ndr_pull_dcerpc_auth ( ndr , NDR_SCALARS | NDR_BUFFERS , auth ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
talloc_free ( ndr ) ;
2015-06-28 02:19:57 +03:00
ZERO_STRUCTP ( auth ) ;
2010-07-10 02:23:49 +04:00
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2016-06-20 17:25:12 +03:00
/*
* Make sure the padding would not exceed
* the frag_length .
*
* Here we assume at least 24 bytes for the
* payload specific header the value of
* DCERPC_ { REQUEST , RESPONSE } _LENGTH .
*
* We use this also for BIND_ * , ALTER_ * and AUTH3 pdus .
*
* We need this check before we ignore possible
* invalid values . See also bug # 11982.
*
* This check is mainly used to generate the correct
* error for BIND_ * , ALTER_ * and AUTH3 pdus .
*
* We always have the ' if ( data_and_pad < auth - > auth_pad_length ) '
* protection for REQUEST and RESPONSE pdus , where the
* auth_pad_length field is actually used by the caller .
*/
tmp_length = DCERPC_REQUEST_LENGTH ;
tmp_length + = DCERPC_AUTH_TRAILER_LENGTH ;
tmp_length + = pkt - > auth_length ;
if ( tmp_length < pkt - > frag_length ) {
max_pad_len = pkt - > frag_length - tmp_length ;
}
if ( max_pad_len < auth - > auth_pad_length ) {
DEBUG ( 1 , ( __location__ " : ERROR: pad length to large. "
" max %u got %u \n " ,
( unsigned ) max_pad_len ,
( unsigned ) auth - > auth_pad_length ) ) ;
talloc_free ( ndr ) ;
ZERO_STRUCTP ( auth ) ;
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
2016-06-20 17:26:56 +03:00
/*
* This is a workarround for a bug in old
* Samba releases . For BIND_ACK < = 3.5 . x
* and for ALTER_RESP < = 4.2 . x ( see bug # 11061 )
*
* See also bug # 11982.
*/
if ( auth_data_only & & data_and_pad = = 0 & &
auth - > auth_pad_length > 0 ) {
/*
* we need to ignore invalid auth_pad_length
* values for BIND_ * , ALTER_ * and AUTH3 pdus .
*/
auth - > auth_pad_length = 0 ;
}
2015-06-28 02:19:57 +03:00
if ( data_and_pad < auth - > auth_pad_length ) {
DEBUG ( 1 , ( __location__ " : ERROR: pad length mismatch. "
" Calculated %u got %u \n " ,
( unsigned ) data_and_pad ,
( unsigned ) auth - > auth_pad_length ) ) ;
talloc_free ( ndr ) ;
ZERO_STRUCTP ( auth ) ;
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
2010-07-20 02:57:12 +04:00
if ( auth_data_only & & data_and_pad ! = auth - > auth_pad_length ) {
2015-06-28 02:19:57 +03:00
DEBUG ( 1 , ( __location__ " : ERROR: pad length mismatch. "
2010-07-20 02:57:12 +04:00
" Calculated %u got %u \n " ,
( unsigned ) data_and_pad ,
( unsigned ) auth - > auth_pad_length ) ) ;
2015-06-28 02:19:57 +03:00
talloc_free ( ndr ) ;
ZERO_STRUCTP ( auth ) ;
return NT_STATUS_RPC_PROTOCOL_ERROR ;
2010-07-10 02:23:49 +04:00
}
2017-09-07 02:20:27 +03:00
DBG_DEBUG ( " auth_pad_length %u \n " ,
( unsigned ) auth - > auth_pad_length ) ;
2010-07-10 02:23:49 +04:00
talloc_steal ( mem_ctx , auth - > credentials . data ) ;
talloc_free ( ndr ) ;
2015-06-28 02:19:57 +03:00
if ( _auth_length ! = NULL ) {
* _auth_length = auth_length ;
}
2010-07-10 02:23:49 +04:00
return NT_STATUS_OK ;
}
2010-08-12 17:49:32 +04:00
2015-06-26 09:10:46 +03:00
/**
* @ brief Verify the fields in ncacn_packet header .
*
* @ param pkt - The ncacn_packet strcuture
* @ param ptype - The expected PDU type
* @ param max_auth_info - The maximum size of a possible auth trailer
* @ param required_flags - The required flags for the pdu .
* @ param optional_flags - The possible optional flags for the pdu .
*
* @ return - A NTSTATUS error code .
*/
NTSTATUS dcerpc_verify_ncacn_packet_header ( const struct ncacn_packet * pkt ,
enum dcerpc_pkt_type ptype ,
size_t max_auth_info ,
uint8_t required_flags ,
uint8_t optional_flags )
{
if ( pkt - > rpc_vers ! = 5 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > rpc_vers_minor ! = 0 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > auth_length > pkt - > frag_length ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > ptype ! = ptype ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( max_auth_info > UINT16_MAX ) {
return NT_STATUS_INTERNAL_ERROR ;
}
if ( pkt - > auth_length > 0 ) {
size_t max_auth_length ;
if ( max_auth_info < = DCERPC_AUTH_TRAILER_LENGTH ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
max_auth_length = max_auth_info - DCERPC_AUTH_TRAILER_LENGTH ;
if ( pkt - > auth_length > max_auth_length ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
}
if ( ( pkt - > pfc_flags & required_flags ) ! = required_flags ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > pfc_flags & ~ ( optional_flags | required_flags ) ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > drep [ 0 ] & ~ DCERPC_DREP_LE ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > drep [ 1 ] ! = 0 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > drep [ 2 ] ! = 0 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( pkt - > drep [ 3 ] ! = 0 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
return NT_STATUS_OK ;
}
2016-09-06 17:43:53 +03:00
NTSTATUS dcerpc_ncacn_pull_pkt_auth ( const struct dcerpc_auth * auth_state ,
struct gensec_security * gensec ,
TALLOC_CTX * mem_ctx ,
enum dcerpc_pkt_type ptype ,
uint8_t required_flags ,
uint8_t optional_flags ,
uint8_t payload_offset ,
DATA_BLOB * payload_and_verifier ,
DATA_BLOB * raw_packet ,
const struct ncacn_packet * pkt )
{
NTSTATUS status ;
struct dcerpc_auth auth ;
uint32_t auth_length ;
if ( auth_state = = NULL ) {
return NT_STATUS_INTERNAL_ERROR ;
}
status = dcerpc_verify_ncacn_packet_header ( pkt , ptype ,
payload_and_verifier - > length ,
required_flags , optional_flags ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
switch ( auth_state - > auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
case DCERPC_AUTH_LEVEL_INTEGRITY :
case DCERPC_AUTH_LEVEL_PACKET :
break ;
case DCERPC_AUTH_LEVEL_CONNECT :
if ( pkt - > auth_length ! = 0 ) {
break ;
}
return NT_STATUS_OK ;
case DCERPC_AUTH_LEVEL_NONE :
if ( pkt - > auth_length ! = 0 ) {
return NT_STATUS_ACCESS_DENIED ;
}
return NT_STATUS_OK ;
default :
return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL ;
}
if ( pkt - > auth_length = = 0 ) {
return NT_STATUS_RPC_PROTOCOL_ERROR ;
}
if ( gensec = = NULL ) {
return NT_STATUS_INTERNAL_ERROR ;
}
status = dcerpc_pull_auth_trailer ( pkt , mem_ctx ,
payload_and_verifier ,
& auth , & auth_length , false ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( payload_and_verifier - > length < auth_length ) {
/*
* should be checked in dcerpc_pull_auth_trailer ( )
*/
return NT_STATUS_INTERNAL_ERROR ;
}
payload_and_verifier - > length - = auth_length ;
if ( payload_and_verifier - > length < auth . auth_pad_length ) {
/*
* should be checked in dcerpc_pull_auth_trailer ( )
*/
return NT_STATUS_INTERNAL_ERROR ;
}
if ( auth . auth_type ! = auth_state - > auth_type ) {
return NT_STATUS_ACCESS_DENIED ;
}
if ( auth . auth_level ! = auth_state - > auth_level ) {
return NT_STATUS_ACCESS_DENIED ;
}
if ( auth . auth_context_id ! = auth_state - > auth_context_id ) {
return NT_STATUS_ACCESS_DENIED ;
}
/* check signature or unseal the packet */
switch ( auth_state - > auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
status = gensec_unseal_packet ( gensec ,
raw_packet - > data + payload_offset ,
payload_and_verifier - > length ,
raw_packet - > data ,
raw_packet - > length -
auth . credentials . length ,
& auth . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_RPC_SEC_PKG_ERROR ;
}
memcpy ( payload_and_verifier - > data ,
raw_packet - > data + payload_offset ,
payload_and_verifier - > length ) ;
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
case DCERPC_AUTH_LEVEL_PACKET :
status = gensec_check_packet ( gensec ,
payload_and_verifier - > data ,
payload_and_verifier - > length ,
raw_packet - > data ,
raw_packet - > length -
auth . credentials . length ,
& auth . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_RPC_SEC_PKG_ERROR ;
}
break ;
case DCERPC_AUTH_LEVEL_CONNECT :
/* for now we ignore possible signatures here */
break ;
default :
return NT_STATUS_RPC_UNSUPPORTED_AUTHN_LEVEL ;
}
/*
* remove the indicated amount of padding
*
* A possible overflow is checked above .
*/
payload_and_verifier - > length - = auth . auth_pad_length ;
return NT_STATUS_OK ;
}
2016-09-06 18:17:36 +03:00
NTSTATUS dcerpc_ncacn_push_pkt_auth ( const struct dcerpc_auth * auth_state ,
struct gensec_security * gensec ,
TALLOC_CTX * mem_ctx ,
DATA_BLOB * raw_packet ,
size_t sig_size ,
uint8_t payload_offset ,
const DATA_BLOB * payload ,
const struct ncacn_packet * pkt )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
NTSTATUS status ;
enum ndr_err_code ndr_err ;
struct ndr_push * ndr = NULL ;
uint32_t payload_length ;
uint32_t whole_length ;
DATA_BLOB blob = data_blob_null ;
DATA_BLOB sig = data_blob_null ;
struct dcerpc_auth _out_auth_info ;
struct dcerpc_auth * out_auth_info = NULL ;
* raw_packet = data_blob_null ;
if ( auth_state = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
switch ( auth_state - > auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
case DCERPC_AUTH_LEVEL_INTEGRITY :
case DCERPC_AUTH_LEVEL_PACKET :
if ( sig_size = = 0 ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( gensec = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
_out_auth_info = ( struct dcerpc_auth ) {
. auth_type = auth_state - > auth_type ,
. auth_level = auth_state - > auth_level ,
. auth_context_id = auth_state - > auth_context_id ,
} ;
out_auth_info = & _out_auth_info ;
break ;
case DCERPC_AUTH_LEVEL_CONNECT :
/*
* TODO : let the gensec mech decide if it wants to generate a
* signature that might be needed for schannel . . .
*/
if ( sig_size ! = 0 ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( gensec = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
break ;
case DCERPC_AUTH_LEVEL_NONE :
if ( sig_size ! = 0 ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
break ;
default :
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
ndr = ndr_push_init_ctx ( frame ) ;
if ( ndr = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
ndr_err = ndr_push_ncacn_packet ( ndr , NDR_SCALARS | NDR_BUFFERS , pkt ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
if ( out_auth_info ! = NULL ) {
/*
* pad to 16 byte multiple in the payload portion of the
* packet . This matches what w2k3 does . Note that we can ' t use
* ndr_push_align ( ) as that is relative to the start of the
* whole packet , whereas w2k8 wants it relative to the start
* of the stub .
*/
out_auth_info - > auth_pad_length =
DCERPC_AUTH_PAD_LENGTH ( payload - > length ) ;
ndr_err = ndr_push_zero ( ndr , out_auth_info - > auth_pad_length ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
payload_length = payload - > length +
out_auth_info - > auth_pad_length ;
ndr_err = ndr_push_dcerpc_auth ( ndr , NDR_SCALARS | NDR_BUFFERS ,
out_auth_info ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
whole_length = ndr - > offset ;
ndr_err = ndr_push_zero ( ndr , sig_size ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
} else {
payload_length = payload - > length ;
whole_length = ndr - > offset ;
}
/* extract the whole packet as a blob */
blob = ndr_push_blob ( ndr ) ;
/*
* Setup the frag and auth length in the packet buffer .
* This is needed if the GENSEC mech does AEAD signing
* of the packet headers . The signature itself will be
* appended later .
*/
dcerpc_set_frag_length ( & blob , blob . length ) ;
dcerpc_set_auth_length ( & blob , sig_size ) ;
/* sign or seal the packet */
switch ( auth_state - > auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
status = gensec_seal_packet ( gensec ,
frame ,
blob . data + payload_offset ,
payload_length ,
blob . data ,
whole_length ,
& sig ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return status ;
}
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
case DCERPC_AUTH_LEVEL_PACKET :
status = gensec_sign_packet ( gensec ,
frame ,
blob . data + payload_offset ,
payload_length ,
blob . data ,
whole_length ,
& sig ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return status ;
}
break ;
case DCERPC_AUTH_LEVEL_CONNECT :
case DCERPC_AUTH_LEVEL_NONE :
break ;
default :
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_ERROR ;
}
if ( sig . length ! = sig_size ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_RPC_SEC_PKG_ERROR ;
}
if ( sig_size ! = 0 ) {
memcpy ( blob . data + whole_length , sig . data , sig_size ) ;
}
* raw_packet = blob ;
talloc_steal ( mem_ctx , raw_packet - > data ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}
2010-08-12 17:49:32 +04:00
struct dcerpc_read_ncacn_packet_state {
#if 0
struct {
} caller ;
# endif
DATA_BLOB buffer ;
struct ncacn_packet * pkt ;
} ;
static int dcerpc_read_ncacn_packet_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void dcerpc_read_ncacn_packet_done ( struct tevent_req * subreq ) ;
struct tevent_req * dcerpc_read_ncacn_packet_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream )
{
struct tevent_req * req ;
struct dcerpc_read_ncacn_packet_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct dcerpc_read_ncacn_packet_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2015-10-28 14:27:43 +03:00
state - > pkt = talloc_zero ( state , struct ncacn_packet ) ;
2010-08-12 17:49:32 +04:00
if ( tevent_req_nomem ( state - > pkt , req ) ) {
goto post ;
}
subreq = tstream_readv_pdu_send ( state , ev ,
stream ,
dcerpc_read_ncacn_packet_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
goto post ;
}
tevent_req_set_callback ( subreq , dcerpc_read_ncacn_packet_done , req ) ;
return req ;
post :
tevent_req_post ( req , ev ) ;
return req ;
}
static int dcerpc_read_ncacn_packet_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct dcerpc_read_ncacn_packet_state * state =
talloc_get_type_abort ( private_data ,
struct dcerpc_read_ncacn_packet_state ) ;
struct iovec * vector ;
off_t ofs = 0 ;
if ( state - > buffer . length = = 0 ) {
2013-09-11 14:23:27 +04:00
/*
* first get enough to read the fragment length
*
* We read the full fixed ncacn_packet header
* in order to make wireshark happy with
* pcap files from socket_wrapper .
*/
2010-08-12 17:49:32 +04:00
ofs = 0 ;
2013-09-11 14:23:27 +04:00
state - > buffer . length = DCERPC_NCACN_PAYLOAD_OFFSET ;
2010-08-12 17:49:32 +04:00
state - > buffer . data = talloc_array ( state , uint8_t ,
state - > buffer . length ) ;
if ( ! state - > buffer . data ) {
return - 1 ;
}
2013-09-11 14:23:27 +04:00
} else if ( state - > buffer . length = = DCERPC_NCACN_PAYLOAD_OFFSET ) {
2010-08-12 17:49:32 +04:00
/* now read the fragment length and allocate the full buffer */
size_t frag_len = dcerpc_get_frag_length ( & state - > buffer ) ;
ofs = state - > buffer . length ;
2013-09-24 07:03:40 +04:00
if ( frag_len < ofs ) {
/*
* something is wrong , let the caller deal with it
*/
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
2010-08-12 17:49:32 +04:00
state - > buffer . data = talloc_realloc ( state ,
state - > buffer . data ,
uint8_t , frag_len ) ;
if ( ! state - > buffer . data ) {
return - 1 ;
}
state - > buffer . length = frag_len ;
} else {
/* if we reach this we have a full fragment */
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
/* now create the vector that we want to be filled */
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( ! vector ) {
return - 1 ;
}
vector [ 0 ] . iov_base = ( void * ) ( state - > buffer . data + ofs ) ;
vector [ 0 ] . iov_len = state - > buffer . length - ofs ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
static void dcerpc_read_ncacn_packet_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct dcerpc_read_ncacn_packet_state * state = tevent_req_data ( req ,
struct dcerpc_read_ncacn_packet_state ) ;
int ret ;
int sys_errno ;
NTSTATUS status ;
ret = tstream_readv_pdu_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
2011-06-20 08:55:32 +04:00
status = map_nt_error_from_unix_common ( sys_errno ) ;
2010-08-12 17:49:32 +04:00
tevent_req_nterror ( req , status ) ;
return ;
}
2015-10-28 14:27:43 +03:00
status = dcerpc_pull_ncacn_packet ( state - > pkt ,
& state - > buffer ,
state - > pkt ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-09-24 07:03:40 +04:00
return ;
}
2010-08-12 17:49:32 +04:00
tevent_req_done ( req ) ;
}
NTSTATUS dcerpc_read_ncacn_packet_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct ncacn_packet * * pkt ,
DATA_BLOB * buffer )
{
struct dcerpc_read_ncacn_packet_state * state = tevent_req_data ( req ,
struct dcerpc_read_ncacn_packet_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
* pkt = talloc_move ( mem_ctx , & state - > pkt ) ;
if ( buffer ) {
buffer - > data = talloc_move ( mem_ctx , & state - > buffer . data ) ;
buffer - > length = state - > buffer . length ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2013-08-08 19:33:29 +04:00
const char * dcerpc_default_transport_endpoint ( TALLOC_CTX * mem_ctx ,
enum dcerpc_transport_t transport ,
const struct ndr_interface_table * table )
{
NTSTATUS status ;
const char * p = NULL ;
const char * endpoint = NULL ;
int i ;
struct dcerpc_binding * default_binding = NULL ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
/* Find one of the default pipes for this interface */
for ( i = 0 ; i < table - > endpoints - > count ; i + + ) {
2014-01-30 12:09:04 +04:00
enum dcerpc_transport_t dtransport ;
const char * dendpoint ;
2013-08-08 19:33:29 +04:00
status = dcerpc_parse_binding ( frame , table - > endpoints - > names [ i ] ,
& default_binding ) ;
2014-01-30 12:09:04 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
continue ;
2013-08-08 19:33:29 +04:00
}
2014-01-30 12:09:04 +04:00
dtransport = dcerpc_binding_get_transport ( default_binding ) ;
dendpoint = dcerpc_binding_get_string_option ( default_binding ,
" endpoint " ) ;
if ( dendpoint = = NULL ) {
TALLOC_FREE ( default_binding ) ;
continue ;
}
if ( transport = = NCA_UNKNOWN ) {
transport = dtransport ;
}
if ( transport ! = dtransport ) {
TALLOC_FREE ( default_binding ) ;
continue ;
}
p = dendpoint ;
break ;
2013-08-08 19:33:29 +04:00
}
2014-01-30 12:09:04 +04:00
if ( p = = NULL ) {
2013-08-08 19:33:29 +04:00
goto done ;
}
/*
* extract the pipe name without \ \ pipe from for example
* ncacn_np : [ \ \ pipe \ \ epmapper ]
*/
2014-01-30 12:09:04 +04:00
if ( transport = = NCACN_NP ) {
2013-08-08 19:33:29 +04:00
if ( strncasecmp ( p , " \\ pipe \\ " , 6 ) = = 0 ) {
p + = 6 ;
}
if ( strncmp ( p , " \\ " , 1 ) = = 0 ) {
p + = 1 ;
}
}
endpoint = talloc_strdup ( mem_ctx , p ) ;
done :
talloc_free ( frame ) ;
return endpoint ;
}
2014-01-13 16:33:09 +04:00
struct dcerpc_sec_vt_header2 dcerpc_sec_vt_header2_from_ncacn_packet ( const struct ncacn_packet * pkt )
{
struct dcerpc_sec_vt_header2 ret ;
ZERO_STRUCT ( ret ) ;
ret . ptype = pkt - > ptype ;
memcpy ( & ret . drep , pkt - > drep , sizeof ( ret . drep ) ) ;
ret . call_id = pkt - > call_id ;
switch ( pkt - > ptype ) {
case DCERPC_PKT_REQUEST :
ret . context_id = pkt - > u . request . context_id ;
ret . opnum = pkt - > u . request . opnum ;
break ;
case DCERPC_PKT_RESPONSE :
ret . context_id = pkt - > u . response . context_id ;
break ;
case DCERPC_PKT_FAULT :
ret . context_id = pkt - > u . fault . context_id ;
break ;
default :
break ;
}
return ret ;
}
bool dcerpc_sec_vt_header2_equal ( const struct dcerpc_sec_vt_header2 * v1 ,
const struct dcerpc_sec_vt_header2 * v2 )
{
if ( v1 - > ptype ! = v2 - > ptype ) {
return false ;
}
if ( memcmp ( v1 - > drep , v2 - > drep , sizeof ( v1 - > drep ) ) ! = 0 ) {
return false ;
}
if ( v1 - > call_id ! = v2 - > call_id ) {
return false ;
}
if ( v1 - > context_id ! = v2 - > context_id ) {
return false ;
}
if ( v1 - > opnum ! = v2 - > opnum ) {
return false ;
}
return true ;
}
2014-01-08 12:50:33 +04:00
static bool dcerpc_sec_vt_is_valid ( const struct dcerpc_sec_verification_trailer * r )
{
bool ret = false ;
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct bitmap * commands_seen ;
int i ;
if ( r - > count . count = = 0 ) {
ret = true ;
goto done ;
}
if ( memcmp ( r - > magic , DCERPC_SEC_VT_MAGIC , sizeof ( r - > magic ) ) ! = 0 ) {
goto done ;
}
commands_seen = bitmap_talloc ( frame , DCERPC_SEC_VT_COMMAND_ENUM + 1 ) ;
if ( commands_seen = = NULL ) {
goto done ;
}
for ( i = 0 ; i < r - > count . count ; i + + ) {
enum dcerpc_sec_vt_command_enum cmd =
r - > commands [ i ] . command & DCERPC_SEC_VT_COMMAND_ENUM ;
if ( bitmap_query ( commands_seen , cmd ) ) {
/* Each command must appear at most once. */
goto done ;
}
bitmap_set ( commands_seen , cmd ) ;
switch ( cmd ) {
case DCERPC_SEC_VT_COMMAND_BITMASK1 :
case DCERPC_SEC_VT_COMMAND_PCONTEXT :
case DCERPC_SEC_VT_COMMAND_HEADER2 :
break ;
default :
if ( ( r - > commands [ i ] . u . _unknown . length % 4 ) ! = 0 ) {
goto done ;
}
break ;
}
}
ret = true ;
done :
TALLOC_FREE ( frame ) ;
return ret ;
}
2014-03-24 19:46:08 +04:00
static bool dcerpc_sec_vt_bitmask_check ( const uint32_t * bitmask1 ,
struct dcerpc_sec_vt * c )
{
if ( bitmask1 = = NULL ) {
2014-03-24 19:46:09 +04:00
if ( c - > command & DCERPC_SEC_VT_MUST_PROCESS ) {
DEBUG ( 10 , ( " SEC_VT check Bitmask1 must_process_command "
" failed \n " ) ) ;
return false ;
}
2014-03-24 19:46:08 +04:00
return true ;
}
2014-03-24 19:46:09 +04:00
if ( ( c - > u . bitmask1 & DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING )
& & ( ! ( * bitmask1 & DCERPC_SEC_VT_CLIENT_SUPPORTS_HEADER_SIGNING ) ) ) {
DEBUG ( 10 , ( " SEC_VT check Bitmask1 client_header_signing "
" failed \n " ) ) ;
return false ;
2014-03-24 19:46:08 +04:00
}
return true ;
}
static bool dcerpc_sec_vt_pctx_check ( const struct dcerpc_sec_vt_pcontext * pcontext ,
struct dcerpc_sec_vt * c )
{
2014-03-24 19:46:10 +04:00
TALLOC_CTX * mem_ctx ;
bool ok ;
2014-03-24 19:46:08 +04:00
if ( pcontext = = NULL ) {
2014-03-24 19:46:09 +04:00
if ( c - > command & DCERPC_SEC_VT_MUST_PROCESS ) {
DEBUG ( 10 , ( " SEC_VT check Pcontext must_process_command "
" failed \n " ) ) ;
return false ;
}
2014-03-24 19:46:08 +04:00
return true ;
}
2014-03-24 19:46:10 +04:00
mem_ctx = talloc_stackframe ( ) ;
ok = ndr_syntax_id_equal ( & pcontext - > abstract_syntax ,
& c - > u . pcontext . abstract_syntax ) ;
if ( ! ok ) {
DEBUG ( 10 , ( " SEC_VT check pcontext abstract_syntax failed: "
" %s vs. %s \n " ,
ndr_syntax_id_to_string ( mem_ctx ,
& pcontext - > abstract_syntax ) ,
ndr_syntax_id_to_string ( mem_ctx ,
& c - > u . pcontext . abstract_syntax ) ) ) ;
goto err_ctx_free ;
}
ok = ndr_syntax_id_equal ( & pcontext - > transfer_syntax ,
& c - > u . pcontext . transfer_syntax ) ;
if ( ! ok ) {
DEBUG ( 10 , ( " SEC_VT check pcontext transfer_syntax failed: "
" %s vs. %s \n " ,
ndr_syntax_id_to_string ( mem_ctx ,
& pcontext - > transfer_syntax ) ,
ndr_syntax_id_to_string ( mem_ctx ,
& c - > u . pcontext . transfer_syntax ) ) ) ;
goto err_ctx_free ;
}
ok = true ;
err_ctx_free :
talloc_free ( mem_ctx ) ;
return ok ;
2014-03-24 19:46:08 +04:00
}
static bool dcerpc_sec_vt_hdr2_check ( const struct dcerpc_sec_vt_header2 * header2 ,
struct dcerpc_sec_vt * c )
{
if ( header2 = = NULL ) {
2014-03-24 19:46:09 +04:00
if ( c - > command & DCERPC_SEC_VT_MUST_PROCESS ) {
DEBUG ( 10 , ( " SEC_VT check Header2 must_process_command failed \n " ) ) ;
return false ;
}
2014-03-24 19:46:08 +04:00
return true ;
}
2014-03-24 19:46:09 +04:00
if ( ! dcerpc_sec_vt_header2_equal ( header2 , & c - > u . header2 ) ) {
DEBUG ( 10 , ( " SEC_VT check Header2 failed \n " ) ) ;
return false ;
}
2014-03-24 19:46:08 +04:00
return true ;
}
2014-01-08 12:50:33 +04:00
bool dcerpc_sec_verification_trailer_check (
const struct dcerpc_sec_verification_trailer * vt ,
const uint32_t * bitmask1 ,
const struct dcerpc_sec_vt_pcontext * pcontext ,
const struct dcerpc_sec_vt_header2 * header2 )
{
size_t i ;
if ( ! dcerpc_sec_vt_is_valid ( vt ) ) {
return false ;
}
for ( i = 0 ; i < vt - > count . count ; i + + ) {
2014-03-24 19:46:08 +04:00
bool ok ;
2014-01-08 12:50:33 +04:00
struct dcerpc_sec_vt * c = & vt - > commands [ i ] ;
switch ( c - > command & DCERPC_SEC_VT_COMMAND_ENUM ) {
case DCERPC_SEC_VT_COMMAND_BITMASK1 :
2014-03-24 19:46:08 +04:00
ok = dcerpc_sec_vt_bitmask_check ( bitmask1 , c ) ;
if ( ! ok ) {
return false ;
2014-01-08 12:50:33 +04:00
}
break ;
case DCERPC_SEC_VT_COMMAND_PCONTEXT :
2014-03-24 19:46:08 +04:00
ok = dcerpc_sec_vt_pctx_check ( pcontext , c ) ;
if ( ! ok ) {
return false ;
2014-01-08 12:50:33 +04:00
}
break ;
case DCERPC_SEC_VT_COMMAND_HEADER2 : {
2014-03-24 19:46:08 +04:00
ok = dcerpc_sec_vt_hdr2_check ( header2 , c ) ;
if ( ! ok ) {
return false ;
2014-01-08 12:50:33 +04:00
}
break ;
}
default :
2014-03-24 19:46:09 +04:00
if ( c - > command & DCERPC_SEC_VT_MUST_PROCESS ) {
DEBUG ( 10 , ( " SEC_VT check Unknown must_process_command failed \n " ) ) ;
return false ;
}
2014-01-08 12:50:33 +04:00
break ;
}
}
return true ;
}
2014-01-09 15:35:58 +04:00
static const struct ndr_syntax_id dcerpc_bind_time_features_prefix = {
. uuid = {
. time_low = 0x6cb71c2c ,
. time_mid = 0x9812 ,
. time_hi_and_version = 0x4540 ,
. clock_seq = { 0x00 , 0x00 } ,
. node = { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 }
} ,
. if_version = 1 ,
} ;
bool dcerpc_extract_bind_time_features ( struct ndr_syntax_id s , uint64_t * _features )
{
uint8_t values [ 8 ] ;
uint64_t features = 0 ;
values [ 0 ] = s . uuid . clock_seq [ 0 ] ;
values [ 1 ] = s . uuid . clock_seq [ 1 ] ;
values [ 2 ] = s . uuid . node [ 0 ] ;
values [ 3 ] = s . uuid . node [ 1 ] ;
values [ 4 ] = s . uuid . node [ 2 ] ;
values [ 5 ] = s . uuid . node [ 3 ] ;
values [ 6 ] = s . uuid . node [ 4 ] ;
values [ 7 ] = s . uuid . node [ 5 ] ;
ZERO_STRUCT ( s . uuid . clock_seq ) ;
ZERO_STRUCT ( s . uuid . node ) ;
if ( ! ndr_syntax_id_equal ( & s , & dcerpc_bind_time_features_prefix ) ) {
if ( _features ! = NULL ) {
* _features = 0 ;
}
return false ;
}
features = BVAL ( values , 0 ) ;
if ( _features ! = NULL ) {
* _features = features ;
}
return true ;
}
struct ndr_syntax_id dcerpc_construct_bind_time_features ( uint64_t features )
{
struct ndr_syntax_id s = dcerpc_bind_time_features_prefix ;
uint8_t values [ 8 ] ;
SBVAL ( values , 0 , features ) ;
s . uuid . clock_seq [ 0 ] = values [ 0 ] ;
s . uuid . clock_seq [ 1 ] = values [ 1 ] ;
s . uuid . node [ 0 ] = values [ 2 ] ;
s . uuid . node [ 1 ] = values [ 3 ] ;
s . uuid . node [ 2 ] = values [ 4 ] ;
s . uuid . node [ 3 ] = values [ 5 ] ;
s . uuid . node [ 4 ] = values [ 6 ] ;
s . uuid . node [ 5 ] = values [ 7 ] ;
return s ;
}