2010-07-14 20:18:49 +04:00
/*
* DCERPC Helper routines
* Günther Deschner < gd @ samba . org > 2010.
* Simo Sorce < idra @ samba . org > 2010.
*
* 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"
# include "librpc/rpc/dcerpc.h"
# include "librpc/gen_ndr/ndr_dcerpc.h"
# include "librpc/gen_ndr/ndr_schannel.h"
2010-07-20 00:10:35 +04:00
# include "../libcli/auth/schannel.h"
# include "../libcli/auth/spnego.h"
2010-09-01 19:58:33 +04:00
# include "librpc/crypto/gse.h"
2010-09-03 01:50:21 +04:00
# include "librpc/crypto/spnego.h"
2011-10-18 14:27:39 +04:00
# include "auth/gensec/gensec.h"
2010-07-14 20:18:49 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_RPC_PARSE
/**
* @ brief NDR Encodes a ncacn_packet
*
* @ param mem_ctx The memory context the blob will be allocated on
* @ param ptype The DCERPC packet type
* @ param pfc_flags The DCERPC PFC Falgs
* @ param auth_length The length of the trailing auth blob
* @ param call_id The call ID
* @ param u The payload of the packet
* @ param blob [ out ] The encoded blob if successful
*
* @ return an NTSTATUS error code
*/
NTSTATUS dcerpc_push_ncacn_packet ( TALLOC_CTX * mem_ctx ,
enum dcerpc_pkt_type ptype ,
uint8_t pfc_flags ,
uint16_t auth_length ,
uint32_t call_id ,
union dcerpc_payload * u ,
DATA_BLOB * blob )
{
struct ncacn_packet r ;
enum ndr_err_code ndr_err ;
r . rpc_vers = 5 ;
r . rpc_vers_minor = 0 ;
r . ptype = ptype ;
r . pfc_flags = pfc_flags ;
r . drep [ 0 ] = DCERPC_DREP_LE ;
r . drep [ 1 ] = 0 ;
r . drep [ 2 ] = 0 ;
r . drep [ 3 ] = 0 ;
r . auth_length = auth_length ;
r . call_id = call_id ;
r . u = * u ;
ndr_err = ndr_push_struct_blob ( blob , mem_ctx , & r ,
( ndr_push_flags_fn_t ) ndr_push_ncacn_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
dcerpc_set_frag_length ( blob , blob - > length ) ;
if ( DEBUGLEVEL > = 10 ) {
/* set frag len for print function */
r . frag_length = blob - > length ;
NDR_PRINT_DEBUG ( ncacn_packet , & r ) ;
}
return NT_STATUS_OK ;
}
/**
* @ 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
2010-08-02 18:03:04 +04:00
* @ param bigendian Whether the packet is bignedian encoded
2010-07-14 20:18:49 +04:00
*
* @ return a NTSTATUS error code
*/
NTSTATUS dcerpc_pull_ncacn_packet ( TALLOC_CTX * mem_ctx ,
const DATA_BLOB * blob ,
2010-07-16 23:15:48 +04:00
struct ncacn_packet * r ,
bool bigendian )
2010-07-14 20:18:49 +04:00
{
enum ndr_err_code ndr_err ;
2010-07-16 23:15:48 +04:00
struct ndr_pull * ndr ;
ndr = ndr_pull_init_blob ( blob , mem_ctx ) ;
if ( ! ndr ) {
return NT_STATUS_NO_MEMORY ;
}
if ( bigendian ) {
ndr - > flags | = LIBNDR_FLAG_BIGENDIAN ;
}
ndr_err = ndr_pull_ncacn_packet ( ndr , NDR_SCALARS | NDR_BUFFERS , r ) ;
2010-07-14 20:18:49 +04:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2010-07-16 23:15:48 +04:00
talloc_free ( ndr ) ;
2010-07-14 20:18:49 +04:00
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2010-07-16 23:15:48 +04:00
talloc_free ( ndr ) ;
2010-07-14 20:18:49 +04:00
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( ncacn_packet , r ) ;
}
return NT_STATUS_OK ;
}
/**
* @ brief NDR Encodes a NL_AUTH_MESSAGE
*
* @ param mem_ctx The memory context the blob will be allocated on
* @ param r The NL_AUTH_MESSAGE to encode
* @ param blob [ out ] The encoded blob if successful
*
* @ return a NTSTATUS error code
*/
NTSTATUS dcerpc_push_schannel_bind ( TALLOC_CTX * mem_ctx ,
struct NL_AUTH_MESSAGE * r ,
DATA_BLOB * blob )
{
enum ndr_err_code ndr_err ;
ndr_err = ndr_push_struct_blob ( blob , mem_ctx , r ,
( ndr_push_flags_fn_t ) ndr_push_NL_AUTH_MESSAGE ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( NL_AUTH_MESSAGE , r ) ;
}
return NT_STATUS_OK ;
}
/**
* @ brief NDR Encodes a dcerpc_auth structure
*
* @ param mem_ctx The memory context the blob will be allocated on
* @ param auth_type The DCERPC Authentication Type
* @ param auth_level The DCERPC Authentication Level
* @ param auth_pad_length The padding added to the packet this blob will be
* appended to .
* @ param auth_context_id The context id
* @ param credentials The authentication credentials blob ( signature )
* @ param blob [ out ] The encoded blob if successful
*
* @ return a NTSTATUS error code
*/
NTSTATUS dcerpc_push_dcerpc_auth ( TALLOC_CTX * mem_ctx ,
enum dcerpc_AuthType auth_type ,
enum dcerpc_AuthLevel auth_level ,
uint8_t auth_pad_length ,
uint32_t auth_context_id ,
const DATA_BLOB * credentials ,
DATA_BLOB * blob )
{
struct dcerpc_auth r ;
enum ndr_err_code ndr_err ;
r . auth_type = auth_type ;
r . auth_level = auth_level ;
r . auth_pad_length = auth_pad_length ;
r . auth_reserved = 0 ;
r . auth_context_id = auth_context_id ;
r . credentials = * credentials ;
ndr_err = ndr_push_struct_blob ( blob , mem_ctx , & r ,
( ndr_push_flags_fn_t ) ndr_push_dcerpc_auth ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
return ndr_map_error2ntstatus ( ndr_err ) ;
}
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( dcerpc_auth , & r ) ;
}
return NT_STATUS_OK ;
}
/**
* @ brief Decodes a dcerpc_auth blob
*
* @ 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 dcerpc_auth structure , must not be NULL
*
* @ return a NTSTATUS error code
*/
NTSTATUS dcerpc_pull_dcerpc_auth ( TALLOC_CTX * mem_ctx ,
const DATA_BLOB * blob ,
2010-07-16 23:15:48 +04:00
struct dcerpc_auth * r ,
bool bigendian )
2010-07-14 20:18:49 +04:00
{
enum ndr_err_code ndr_err ;
2010-07-16 23:15:48 +04:00
struct ndr_pull * ndr ;
ndr = ndr_pull_init_blob ( blob , mem_ctx ) ;
if ( ! ndr ) {
return NT_STATUS_NO_MEMORY ;
}
if ( bigendian ) {
ndr - > flags | = LIBNDR_FLAG_BIGENDIAN ;
}
ndr_err = ndr_pull_dcerpc_auth ( ndr , NDR_SCALARS | NDR_BUFFERS , r ) ;
2010-07-14 20:18:49 +04:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2010-07-16 23:15:48 +04:00
talloc_free ( ndr ) ;
2010-07-14 20:18:49 +04:00
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2010-07-16 23:15:48 +04:00
talloc_free ( ndr ) ;
2010-07-14 20:18:49 +04:00
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( dcerpc_auth , r ) ;
}
return NT_STATUS_OK ;
}
2010-07-20 00:10:35 +04:00
2010-07-30 19:27:40 +04:00
/**
* @ brief Calculate how much data we can in a packet , including calculating
* auth token and pad lengths .
*
* @ param auth The pipe_auth_data structure for this pipe .
2010-07-30 21:12:35 +04:00
* @ param header_len The length of the packet header
2010-07-30 19:27:40 +04:00
* @ param data_left The data left in the send buffer
2010-07-30 21:12:35 +04:00
* @ param max_xmit_frag The max fragment size .
* @ param pad_alignment The NDR padding size .
* @ param data_to_send [ out ] The max data we will send in the pdu
* @ param frag_len [ out ] The total length of the fragment
* @ param auth_len [ out ] The length of the auth trailer
* @ param pad_len [ out ] The padding to be applied
2010-07-30 19:27:40 +04:00
*
* @ return A NT Error status code .
*/
NTSTATUS dcerpc_guess_sizes ( struct pipe_auth_data * auth ,
2010-07-30 21:12:35 +04:00
size_t header_len , size_t data_left ,
size_t max_xmit_frag , size_t pad_alignment ,
2010-07-30 19:27:40 +04:00
size_t * data_to_send , size_t * frag_len ,
size_t * auth_len , size_t * pad_len )
{
size_t max_len ;
size_t mod_len ;
2011-12-21 08:54:20 +04:00
struct gensec_security * gensec_security ;
2009-09-26 04:59:59 +04:00
struct schannel_state * schannel_auth ;
2010-09-04 00:27:47 +04:00
struct spnego_context * spnego_ctx ;
2010-07-30 19:27:40 +04:00
struct gse_context * gse_ctx ;
2010-09-03 01:43:21 +04:00
enum spnego_mech auth_type ;
2010-07-30 19:27:40 +04:00
void * auth_ctx ;
bool seal = false ;
NTSTATUS status ;
/* no auth token cases first */
switch ( auth - > auth_level ) {
case DCERPC_AUTH_LEVEL_NONE :
case DCERPC_AUTH_LEVEL_CONNECT :
case DCERPC_AUTH_LEVEL_PACKET :
2010-07-30 21:12:35 +04:00
max_len = max_xmit_frag - header_len ;
* data_to_send = MIN ( max_len , data_left ) ;
2010-07-30 19:27:40 +04:00
* pad_len = 0 ;
* auth_len = 0 ;
2010-07-30 21:12:35 +04:00
* frag_len = header_len + * data_to_send ;
2010-07-30 19:27:40 +04:00
return NT_STATUS_OK ;
case DCERPC_AUTH_LEVEL_PRIVACY :
seal = true ;
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
/* Sign/seal case, calculate auth and pad lengths */
2010-07-30 21:12:35 +04:00
max_len = max_xmit_frag - header_len - DCERPC_AUTH_TRAILER_LENGTH ;
2010-07-30 19:27:40 +04:00
/* Treat the same for all authenticated rpc requests. */
switch ( auth - > auth_type ) {
case DCERPC_AUTH_TYPE_SPNEGO :
2010-09-04 00:27:47 +04:00
spnego_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct spnego_context ) ;
status = spnego_get_negotiated_mech ( spnego_ctx ,
2010-07-30 19:27:40 +04:00
& auth_type , & auth_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
switch ( auth_type ) {
2010-09-03 01:43:21 +04:00
case SPNEGO_NTLMSSP :
2011-12-21 08:54:20 +04:00
gensec_security = talloc_get_type_abort ( auth_ctx ,
struct gensec_security ) ;
* auth_len = gensec_sig_size ( gensec_security , max_len ) ;
2010-07-30 19:27:40 +04:00
break ;
2010-09-03 01:43:21 +04:00
case SPNEGO_KRB5 :
2010-09-04 00:27:47 +04:00
gse_ctx = talloc_get_type_abort ( auth_ctx ,
struct gse_context ) ;
2010-07-30 19:27:40 +04:00
if ( ! gse_ctx ) {
return NT_STATUS_INVALID_PARAMETER ;
}
* auth_len = gse_get_signature_length ( gse_ctx ,
seal , max_len ) ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
break ;
case DCERPC_AUTH_TYPE_NTLMSSP :
2011-12-21 08:54:20 +04:00
gensec_security = talloc_get_type_abort ( auth - > auth_ctx ,
struct gensec_security ) ;
* auth_len = gensec_sig_size ( gensec_security , max_len ) ;
2010-07-30 19:27:40 +04:00
break ;
case DCERPC_AUTH_TYPE_SCHANNEL :
2009-09-26 04:59:59 +04:00
schannel_auth = talloc_get_type_abort ( auth - > auth_ctx ,
struct schannel_state ) ;
* auth_len = netsec_outgoing_sig_size ( schannel_auth ) ;
2010-07-30 19:27:40 +04:00
break ;
case DCERPC_AUTH_TYPE_KRB5 :
2010-09-04 00:27:47 +04:00
gse_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct gse_context ) ;
* auth_len = gse_get_signature_length ( gse_ctx ,
2010-07-30 19:27:40 +04:00
seal , max_len ) ;
break ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
2010-07-30 21:12:35 +04:00
max_len - = * auth_len ;
2010-07-30 19:27:40 +04:00
2010-07-30 21:12:35 +04:00
* data_to_send = MIN ( max_len , data_left ) ;
mod_len = ( header_len + * data_to_send ) % pad_alignment ;
2010-07-30 19:27:40 +04:00
if ( mod_len ) {
2010-07-30 21:12:35 +04:00
* pad_len = pad_alignment - mod_len ;
2010-07-30 19:27:40 +04:00
} else {
* pad_len = 0 ;
}
2010-07-30 21:12:35 +04:00
if ( * data_to_send + * pad_len > max_len ) {
* data_to_send - = pad_alignment ;
}
* frag_len = header_len + * data_to_send + * pad_len
+ DCERPC_AUTH_TRAILER_LENGTH + * auth_len ;
2010-07-30 19:27:40 +04:00
return NT_STATUS_OK ;
}
2010-07-20 00:10:35 +04:00
/*******************************************************************
Create and add the NTLMSSP sign / seal auth data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-10-19 11:39:27 +04:00
static NTSTATUS add_ntlmssp_auth_footer ( struct gensec_security * gensec_security ,
2010-07-20 00:10:35 +04:00
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * rpc_out )
{
uint16_t data_and_pad_len = rpc_out - > length
- DCERPC_RESPONSE_LENGTH
- DCERPC_AUTH_TRAILER_LENGTH ;
DATA_BLOB auth_blob ;
NTSTATUS status ;
2011-10-19 11:39:27 +04:00
if ( ! gensec_security ) {
2010-07-20 00:10:35 +04:00
return NT_STATUS_INVALID_PARAMETER ;
}
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
2011-10-19 11:39:27 +04:00
status = gensec_seal_packet ( gensec_security ,
2011-10-18 14:27:39 +04:00
rpc_out - > data ,
rpc_out - > data
+ DCERPC_RESPONSE_LENGTH ,
data_and_pad_len ,
rpc_out - > data ,
rpc_out - > length ,
& auth_blob ) ;
2010-07-20 00:10:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
2011-10-19 11:39:27 +04:00
status = gensec_sign_packet ( gensec_security ,
2011-10-18 14:27:39 +04:00
rpc_out - > data ,
rpc_out - > data
+ DCERPC_RESPONSE_LENGTH ,
data_and_pad_len ,
rpc_out - > data ,
rpc_out - > length ,
& auth_blob ) ;
2010-07-20 00:10:35 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
break ;
default :
/* Can't happen. */
smb_panic ( " bad auth level " ) ;
/* Notreached. */
return NT_STATUS_INVALID_PARAMETER ;
}
/* Finally attach the blob. */
if ( ! data_blob_append ( NULL , rpc_out ,
auth_blob . data , auth_blob . length ) ) {
DEBUG ( 0 , ( " Failed to add %u bytes auth blob. \n " ,
( unsigned int ) auth_blob . length ) ) ;
return NT_STATUS_NO_MEMORY ;
}
data_blob_free ( & auth_blob ) ;
return NT_STATUS_OK ;
}
2010-07-30 00:34:39 +04:00
/*******************************************************************
Check / unseal the NTLMSSP auth data . ( Unseal in place ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-10-19 11:39:27 +04:00
static NTSTATUS get_ntlmssp_auth_footer ( struct gensec_security * gensec_security ,
2010-07-30 00:34:39 +04:00
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * data , DATA_BLOB * full_pkt ,
DATA_BLOB * auth_token )
{
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
2011-10-19 11:39:27 +04:00
return gensec_unseal_packet ( gensec_security ,
2011-10-18 14:27:39 +04:00
data - > data ,
data - > length ,
full_pkt - > data ,
full_pkt - > length ,
auth_token ) ;
2010-07-30 00:34:39 +04:00
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
2011-10-19 11:39:27 +04:00
return gensec_check_packet ( gensec_security ,
2011-10-18 14:27:39 +04:00
data - > data ,
data - > length ,
full_pkt - > data ,
full_pkt - > length ,
auth_token ) ;
2010-07-30 00:34:39 +04:00
default :
return NT_STATUS_INVALID_PARAMETER ;
}
}
2010-07-20 00:10:35 +04:00
/*******************************************************************
Create and add the schannel sign / seal auth data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS add_schannel_auth_footer ( struct schannel_state * sas ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * rpc_out )
{
uint8_t * data_p = rpc_out - > data + DCERPC_RESPONSE_LENGTH ;
size_t data_and_pad_len = rpc_out - > length
- DCERPC_RESPONSE_LENGTH
- DCERPC_AUTH_TRAILER_LENGTH ;
DATA_BLOB auth_blob ;
NTSTATUS status ;
if ( ! sas ) {
return NT_STATUS_INVALID_PARAMETER ;
}
DEBUG ( 10 , ( " add_schannel_auth_footer: SCHANNEL seq_num=%d \n " ,
sas - > seq_num ) ) ;
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
status = netsec_outgoing_packet ( sas ,
rpc_out - > data ,
true ,
data_p ,
data_and_pad_len ,
& auth_blob ) ;
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
status = netsec_outgoing_packet ( sas ,
rpc_out - > data ,
false ,
data_p ,
data_and_pad_len ,
& auth_blob ) ;
break ;
default :
status = NT_STATUS_INTERNAL_ERROR ;
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " add_schannel_auth_footer: failed to process packet: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
if ( DEBUGLEVEL > = 10 ) {
dump_NL_AUTH_SIGNATURE ( talloc_tos ( ) , & auth_blob ) ;
}
/* Finally attach the blob. */
if ( ! data_blob_append ( NULL , rpc_out ,
auth_blob . data , auth_blob . length ) ) {
return NT_STATUS_NO_MEMORY ;
}
data_blob_free ( & auth_blob ) ;
return NT_STATUS_OK ;
}
2010-07-30 00:34:39 +04:00
/*******************************************************************
Check / unseal the Schannel auth data . ( Unseal in place ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS get_schannel_auth_footer ( TALLOC_CTX * mem_ctx ,
struct schannel_state * auth_state ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * data , DATA_BLOB * full_pkt ,
DATA_BLOB * auth_token )
{
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
return netsec_incoming_packet ( auth_state ,
2011-07-21 13:10:15 +04:00
true ,
2010-07-30 00:34:39 +04:00
data - > data ,
data - > length ,
auth_token ) ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
return netsec_incoming_packet ( auth_state ,
2011-07-21 13:10:15 +04:00
false ,
2010-07-30 00:34:39 +04:00
data - > data ,
data - > length ,
auth_token ) ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
}
2010-07-24 21:02:57 +04:00
/*******************************************************************
Create and add the gssapi sign / seal auth data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS add_gssapi_auth_footer ( struct gse_context * gse_ctx ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * rpc_out )
{
DATA_BLOB data ;
DATA_BLOB auth_blob ;
NTSTATUS status ;
if ( ! gse_ctx ) {
return NT_STATUS_INVALID_PARAMETER ;
}
data . data = rpc_out - > data + DCERPC_RESPONSE_LENGTH ;
data . length = rpc_out - > length - DCERPC_RESPONSE_LENGTH
- DCERPC_AUTH_TRAILER_LENGTH ;
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
status = gse_seal ( talloc_tos ( ) , gse_ctx , & data , & auth_blob ) ;
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
status = gse_sign ( talloc_tos ( ) , gse_ctx , & data , & auth_blob ) ;
break ;
default :
status = NT_STATUS_INTERNAL_ERROR ;
break ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to process packet: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
/* Finally attach the blob. */
if ( ! data_blob_append ( NULL , rpc_out ,
auth_blob . data , auth_blob . length ) ) {
return NT_STATUS_NO_MEMORY ;
}
data_blob_free ( & auth_blob ) ;
return NT_STATUS_OK ;
}
2010-07-30 00:34:39 +04:00
/*******************************************************************
Check / unseal the gssapi auth data . ( Unseal in place ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS get_gssapi_auth_footer ( TALLOC_CTX * mem_ctx ,
struct gse_context * gse_ctx ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * data , DATA_BLOB * full_pkt ,
DATA_BLOB * auth_token )
{
/* TODO: pass in full_pkt when
* DCERPC_PFC_FLAG_SUPPORT_HEADER_SIGN is set */
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
return gse_unseal ( mem_ctx , gse_ctx ,
data , auth_token ) ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
return gse_sigcheck ( mem_ctx , gse_ctx ,
data , auth_token ) ;
default :
return NT_STATUS_INVALID_PARAMETER ;
}
}
/*******************************************************************
Create and add the spnego - negotiated sign / seal auth data .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS add_spnego_auth_footer ( struct spnego_context * spnego_ctx ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * rpc_out )
{
2010-09-11 17:46:08 +04:00
DATA_BLOB auth_blob ;
DATA_BLOB rpc_data ;
2010-07-30 00:34:39 +04:00
NTSTATUS status ;
if ( ! spnego_ctx ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2010-09-11 17:46:08 +04:00
rpc_data = data_blob_const ( rpc_out - > data
+ DCERPC_RESPONSE_LENGTH ,
rpc_out - > length
- DCERPC_RESPONSE_LENGTH
- DCERPC_AUTH_TRAILER_LENGTH ) ;
2010-07-30 00:34:39 +04:00
2010-09-11 17:46:08 +04:00
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
status = spnego_seal ( rpc_out - > data , spnego_ctx ,
& rpc_data , rpc_out , & auth_blob ) ;
break ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-07-30 00:34:39 +04:00
}
break ;
2010-09-11 17:46:08 +04:00
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
status = spnego_sign ( rpc_out - > data , spnego_ctx ,
& rpc_data , rpc_out , & auth_blob ) ;
break ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-07-30 03:55:44 +04:00
}
2010-07-30 00:34:39 +04:00
break ;
default :
2010-09-11 17:46:08 +04:00
/* Can't happen. */
smb_panic ( " bad auth level " ) ;
/* Notreached. */
return NT_STATUS_INVALID_PARAMETER ;
2010-07-30 00:34:39 +04:00
}
2010-09-11 17:46:08 +04:00
/* Finally attach the blob. */
if ( ! data_blob_append ( NULL , rpc_out ,
auth_blob . data , auth_blob . length ) ) {
DEBUG ( 0 , ( " Failed to add %u bytes auth blob. \n " ,
( unsigned int ) auth_blob . length ) ) ;
return NT_STATUS_NO_MEMORY ;
}
data_blob_free ( & auth_blob ) ;
return NT_STATUS_OK ;
2010-07-30 00:34:39 +04:00
}
2010-07-30 03:55:44 +04:00
static NTSTATUS get_spnego_auth_footer ( TALLOC_CTX * mem_ctx ,
struct spnego_context * sp_ctx ,
enum dcerpc_AuthLevel auth_level ,
DATA_BLOB * data , DATA_BLOB * full_pkt ,
DATA_BLOB * auth_token )
{
2010-09-11 17:46:08 +04:00
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
/* Data portion is encrypted. */
return spnego_unseal ( mem_ctx , sp_ctx ,
data , full_pkt , auth_token ) ;
2010-07-30 03:55:44 +04:00
2010-09-11 17:46:08 +04:00
case DCERPC_AUTH_LEVEL_INTEGRITY :
/* Data is signed. */
return spnego_sigcheck ( mem_ctx , sp_ctx ,
data , full_pkt , auth_token ) ;
2010-07-30 03:55:44 +04:00
default :
return NT_STATUS_INVALID_PARAMETER ;
}
}
2010-07-20 00:10:35 +04:00
/**
* @ brief Append an auth footer according to what is the current mechanism
*
* @ param auth The pipe_auth_data associated with the connection
* @ param pad_len The padding used in the packet
* @ param rpc_out Packet blob up to and including the auth header
*
* @ return A NTSTATUS error code .
*/
NTSTATUS dcerpc_add_auth_footer ( struct pipe_auth_data * auth ,
size_t pad_len , DATA_BLOB * rpc_out )
{
2010-09-04 00:27:47 +04:00
struct schannel_state * schannel_auth ;
2011-10-19 11:39:27 +04:00
struct gensec_security * gensec_security ;
2010-09-04 00:27:47 +04:00
struct spnego_context * spnego_ctx ;
struct gse_context * gse_ctx ;
2010-07-20 00:10:35 +04:00
char pad [ CLIENT_NDR_PADDING_SIZE ] = { 0 , } ;
DATA_BLOB auth_info ;
DATA_BLOB auth_blob ;
NTSTATUS status ;
2011-03-01 13:49:20 +03:00
if ( auth - > auth_type = = DCERPC_AUTH_TYPE_NONE | |
auth - > auth_type = = DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM ) {
2010-07-20 00:10:35 +04:00
return NT_STATUS_OK ;
}
if ( pad_len ) {
/* Copy the sign/seal padding data. */
if ( ! data_blob_append ( NULL , rpc_out , pad , pad_len ) ) {
return NT_STATUS_NO_MEMORY ;
}
}
/* marshall the dcerpc_auth with an actually empty auth_blob.
* This is needed because the ntmlssp signature includes the
* auth header . We will append the actual blob later . */
auth_blob = data_blob_null ;
status = dcerpc_push_dcerpc_auth ( rpc_out - > data ,
2010-07-20 21:26:36 +04:00
auth - > auth_type ,
2010-07-20 00:10:35 +04:00
auth - > auth_level ,
pad_len ,
1 /* context id. */ ,
& auth_blob ,
& auth_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* append the header */
if ( ! data_blob_append ( NULL , rpc_out ,
auth_info . data , auth_info . length ) ) {
DEBUG ( 0 , ( " Failed to add %u bytes auth blob. \n " ,
( unsigned int ) auth_info . length ) ) ;
return NT_STATUS_NO_MEMORY ;
}
data_blob_free ( & auth_info ) ;
/* Generate any auth sign/seal and add the auth footer. */
switch ( auth - > auth_type ) {
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_NONE :
2011-03-01 13:49:20 +03:00
case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM :
2010-07-20 00:10:35 +04:00
status = NT_STATUS_OK ;
break ;
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_SPNEGO :
2010-09-04 00:27:47 +04:00
spnego_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct spnego_context ) ;
status = add_spnego_auth_footer ( spnego_ctx ,
2010-07-30 03:55:44 +04:00
auth - > auth_level , rpc_out ) ;
break ;
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_NTLMSSP :
2011-10-19 11:39:27 +04:00
gensec_security = talloc_get_type_abort ( auth - > auth_ctx ,
struct gensec_security ) ;
status = add_ntlmssp_auth_footer ( gensec_security ,
2010-07-20 00:10:35 +04:00
auth - > auth_level ,
rpc_out ) ;
break ;
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_SCHANNEL :
2010-09-04 00:27:47 +04:00
schannel_auth = talloc_get_type_abort ( auth - > auth_ctx ,
struct schannel_state ) ;
status = add_schannel_auth_footer ( schannel_auth ,
2010-07-20 00:10:35 +04:00
auth - > auth_level ,
rpc_out ) ;
break ;
2010-07-24 21:02:57 +04:00
case DCERPC_AUTH_TYPE_KRB5 :
2010-09-04 00:27:47 +04:00
gse_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct gse_context ) ;
status = add_gssapi_auth_footer ( gse_ctx ,
2010-07-24 21:02:57 +04:00
auth - > auth_level ,
rpc_out ) ;
break ;
2010-07-20 00:10:35 +04:00
default :
status = NT_STATUS_INVALID_PARAMETER ;
break ;
}
return status ;
}
2010-07-20 03:49:35 +04:00
/**
* @ brief Check authentication for request / response packets
*
* @ param auth The auth data for the connection
* @ param pkt The actual ncacn_packet
* @ param pkt_trailer The stub_and_verifier part of the packet
* @ param header_size The header size
* @ param raw_pkt The whole raw packet data blob
* @ param pad_len [ out ] The padding length used in the packet
*
* @ return A NTSTATUS error code
*/
NTSTATUS dcerpc_check_auth ( struct pipe_auth_data * auth ,
struct ncacn_packet * pkt ,
DATA_BLOB * pkt_trailer ,
size_t header_size ,
DATA_BLOB * raw_pkt ,
size_t * pad_len )
{
2010-09-04 00:27:47 +04:00
struct schannel_state * schannel_auth ;
2011-10-19 11:39:27 +04:00
struct gensec_security * gensec_security ;
2010-09-04 00:27:47 +04:00
struct spnego_context * spnego_ctx ;
struct gse_context * gse_ctx ;
2010-07-20 03:49:35 +04:00
NTSTATUS status ;
struct dcerpc_auth auth_info ;
uint32_t auth_length ;
DATA_BLOB full_pkt ;
DATA_BLOB data ;
switch ( auth - > auth_level ) {
case DCERPC_AUTH_LEVEL_PRIVACY :
DEBUG ( 10 , ( " Requested Privacy. \n " ) ) ;
break ;
case DCERPC_AUTH_LEVEL_INTEGRITY :
DEBUG ( 10 , ( " Requested Integrity. \n " ) ) ;
break ;
case DCERPC_AUTH_LEVEL_CONNECT :
if ( pkt - > auth_length ! = 0 ) {
break ;
}
* pad_len = 0 ;
return NT_STATUS_OK ;
case DCERPC_AUTH_LEVEL_NONE :
if ( pkt - > auth_length ! = 0 ) {
DEBUG ( 3 , ( " Got non-zero auth len on non "
" authenticated connection! \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
* pad_len = 0 ;
return NT_STATUS_OK ;
default :
DEBUG ( 3 , ( " Unimplemented Auth Level %d " ,
auth - > auth_level ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
/* Paranioa checks for auth_length. */
if ( pkt - > auth_length > pkt - > frag_length ) {
return NT_STATUS_INFO_LENGTH_MISMATCH ;
}
2010-08-13 17:12:29 +04:00
if ( ( ( unsigned int ) pkt - > auth_length
+ DCERPC_AUTH_TRAILER_LENGTH < ( unsigned int ) pkt - > auth_length ) | |
( ( unsigned int ) pkt - > auth_length
2010-07-20 03:49:35 +04:00
+ DCERPC_AUTH_TRAILER_LENGTH < DCERPC_AUTH_TRAILER_LENGTH ) ) {
/* Integer wrap attempt. */
return NT_STATUS_INFO_LENGTH_MISMATCH ;
}
status = dcerpc_pull_auth_trailer ( pkt , pkt , pkt_trailer ,
& auth_info , & auth_length , false ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
data = data_blob_const ( raw_pkt - > data + header_size ,
pkt_trailer - > length - auth_length ) ;
full_pkt = data_blob_const ( raw_pkt - > data ,
raw_pkt - > length - auth_info . credentials . length ) ;
switch ( auth - > auth_type ) {
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_NONE :
2011-03-01 13:49:20 +03:00
case DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM :
2010-07-20 03:49:35 +04:00
return NT_STATUS_OK ;
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_SPNEGO :
2010-09-04 00:27:47 +04:00
spnego_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct spnego_context ) ;
status = get_spnego_auth_footer ( pkt , spnego_ctx ,
2010-07-30 03:55:44 +04:00
auth - > auth_level ,
& data , & full_pkt ,
& auth_info . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
break ;
2010-07-30 00:34:39 +04:00
case DCERPC_AUTH_TYPE_NTLMSSP :
2010-07-20 03:49:35 +04:00
2010-07-30 00:34:39 +04:00
DEBUG ( 10 , ( " NTLMSSP auth \n " ) ) ;
2011-10-19 11:39:27 +04:00
gensec_security = talloc_get_type_abort ( auth - > auth_ctx ,
struct gensec_security ) ;
status = get_ntlmssp_auth_footer ( gensec_security ,
2010-07-30 00:34:39 +04:00
auth - > auth_level ,
& data , & full_pkt ,
& auth_info . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-07-20 03:49:35 +04:00
}
break ;
2010-07-20 21:26:36 +04:00
case DCERPC_AUTH_TYPE_SCHANNEL :
2010-07-20 03:49:35 +04:00
DEBUG ( 10 , ( " SCHANNEL auth \n " ) ) ;
2010-09-04 00:27:47 +04:00
schannel_auth = talloc_get_type_abort ( auth - > auth_ctx ,
struct schannel_state ) ;
status = get_schannel_auth_footer ( pkt , schannel_auth ,
2010-07-30 00:34:39 +04:00
auth - > auth_level ,
& data , & full_pkt ,
& auth_info . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-07-20 03:49:35 +04:00
}
break ;
2010-07-24 21:02:57 +04:00
case DCERPC_AUTH_TYPE_KRB5 :
DEBUG ( 10 , ( " KRB5 auth \n " ) ) ;
2010-09-04 00:27:47 +04:00
gse_ctx = talloc_get_type_abort ( auth - > auth_ctx ,
struct gse_context ) ;
status = get_gssapi_auth_footer ( pkt , gse_ctx ,
2010-07-30 00:34:39 +04:00
auth - > auth_level ,
& data , & full_pkt ,
& auth_info . credentials ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2010-07-24 21:02:57 +04:00
}
break ;
2010-07-20 03:49:35 +04:00
default :
DEBUG ( 0 , ( " process_request_pdu: "
" unknown auth type %u set. \n " ,
( unsigned int ) auth - > auth_type ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2010-07-30 00:34:39 +04:00
/* TODO: remove later
* this is still needed because in the server code the
* pkt_trailer actually has a copy of the raw data , and they
* are still both used in later calls */
if ( auth - > auth_level = = DCERPC_AUTH_LEVEL_PRIVACY ) {
memcpy ( pkt_trailer - > data , data . data , data . length ) ;
}
2010-07-20 03:49:35 +04:00
* pad_len = auth_info . auth_pad_length ;
data_blob_free ( & auth_info . credentials ) ;
return NT_STATUS_OK ;
}