2007-12-27 04:12:36 +03:00
/*
Unix SMB / CIFS implementation .
SMB Transport encryption ( sealing ) code .
Copyright ( C ) Jeremy Allison 2007.
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"
2011-07-25 10:04:38 +04:00
# include "../auth/ntlmssp/ntlmssp.h"
2011-03-02 16:00:23 +03:00
# include "smb_crypt.h"
2011-05-06 13:47:43 +04:00
# include "libsmb/libsmb.h"
2011-07-21 13:13:59 +04:00
# include "ntlmssp_wrap.h"
2011-10-20 15:23:27 +04:00
# include "libcli/auth/krb5_wrap.h"
2011-07-21 13:13:59 +04:00
2007-12-27 04:12:36 +03:00
/******************************************************************************
Pull out the encryption context for this packet . 0 means global context .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-10-19 17:35:23 +04:00
NTSTATUS get_enc_ctx_num ( const uint8_t * buf , uint16_t * p_enc_ctx_num )
2007-12-27 04:12:36 +03:00
{
2011-10-20 11:47:53 +04:00
if ( smb_len_nbt ( buf ) < 8 ) {
2007-12-27 04:12:36 +03:00
return NT_STATUS_INVALID_BUFFER_SIZE ;
}
2008-01-04 23:56:23 +03:00
if ( buf [ 4 ] = = 0xFF ) {
2007-12-27 04:12:36 +03:00
if ( buf [ 5 ] = = ' S ' & & buf [ 6 ] = = ' M ' & & buf [ 7 ] = = ' B ' ) {
/* Not an encrypted buffer. */
return NT_STATUS_NOT_FOUND ;
}
if ( buf [ 5 ] = = ' E ' ) {
* p_enc_ctx_num = SVAL ( buf , 6 ) ;
return NT_STATUS_OK ;
}
}
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2011-10-19 19:37:29 +04:00
/*******************************************************************
Set the length and marker of an encrypted smb packet .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smb_set_enclen ( char * buf , int len , uint16_t enc_ctx_num )
{
2011-10-20 11:47:53 +04:00
_smb_setlen_nbt ( buf , len ) ;
2011-10-19 19:37:29 +04:00
SCVAL ( buf , 4 , 0xFF ) ;
SCVAL ( buf , 5 , ' E ' ) ;
SSVAL ( buf , 6 , enc_ctx_num ) ;
}
2007-12-27 04:12:36 +03:00
/******************************************************************************
Generic code for client and server .
Is encryption turned on ?
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool common_encryption_on ( struct smb_trans_enc_state * es )
{
return ( ( es ! = NULL ) & & es - > enc_on ) ;
}
/******************************************************************************
Generic code for client and server .
NTLM decrypt an incoming buffer .
Abartlett tells me that SSPI puts the signature first before the encrypted
output , so cope with the same for compatibility .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-10-19 17:32:14 +04:00
static NTSTATUS common_ntlm_decrypt_buffer ( struct auth_ntlmssp_state * auth_ntlmssp_state , char * buf )
2007-12-27 04:12:36 +03:00
{
NTSTATUS status ;
2011-10-20 11:47:53 +04:00
size_t buf_len = smb_len_nbt ( buf ) + 4 ; /* Don't forget the 4 length bytes. */
2007-12-27 04:12:36 +03:00
size_t data_len ;
char * inbuf ;
DATA_BLOB sig ;
if ( buf_len < 8 + NTLMSSP_SIG_SIZE ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
inbuf = ( char * ) smb_xmemdup ( buf , buf_len ) ;
/* Adjust for the signature. */
data_len = buf_len - 8 - NTLMSSP_SIG_SIZE ;
/* Point at the signature. */
sig = data_blob_const ( inbuf + 8 , NTLMSSP_SIG_SIZE ) ;
2011-07-21 13:13:59 +04:00
status = auth_ntlmssp_unseal_packet ( auth_ntlmssp_state ,
2007-12-27 04:12:36 +03:00
( unsigned char * ) inbuf + 8 + NTLMSSP_SIG_SIZE , /* 4 byte len + 0xFF 'E' <enc> <ctx> */
data_len ,
( unsigned char * ) inbuf + 8 + NTLMSSP_SIG_SIZE ,
data_len ,
& sig ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
SAFE_FREE ( inbuf ) ;
return status ;
}
memcpy ( buf + 8 , inbuf + 8 + NTLMSSP_SIG_SIZE , data_len ) ;
2008-01-04 23:56:23 +03:00
/* Reset the length and overwrite the header. */
2011-10-20 11:47:53 +04:00
smb_setlen_nbt ( buf , data_len + 4 ) ;
2007-12-27 04:12:36 +03:00
SAFE_FREE ( inbuf ) ;
return NT_STATUS_OK ;
}
/******************************************************************************
Generic code for client and server .
NTLM encrypt an outgoing buffer . Return the encrypted pointer in ppbuf_out .
Abartlett tells me that SSPI puts the signature first before the encrypted
output , so do the same for compatibility .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-10-19 17:32:14 +04:00
static NTSTATUS common_ntlm_encrypt_buffer ( struct auth_ntlmssp_state * auth_ntlmssp_state ,
2011-10-19 17:35:23 +04:00
uint16_t enc_ctx_num ,
2007-12-27 04:12:36 +03:00
char * buf ,
char * * ppbuf_out )
{
NTSTATUS status ;
char * buf_out ;
2011-10-20 11:47:53 +04:00
size_t data_len = smb_len_nbt ( buf ) - 4 ; /* Ignore the 0xFF SMB bytes. */
2007-12-27 04:12:36 +03:00
DATA_BLOB sig ;
2010-05-25 14:55:40 +04:00
TALLOC_CTX * frame ;
2007-12-27 04:12:36 +03:00
* ppbuf_out = NULL ;
if ( data_len = = 0 ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
2010-05-25 14:55:40 +04:00
frame = talloc_stackframe ( ) ;
2007-12-27 04:12:36 +03:00
/*
2011-10-20 11:47:53 +04:00
* We know smb_len_nbt can ' t return a value > 128 k , so no int overflow
2007-12-27 04:12:36 +03:00
* check needed .
*/
buf_out = SMB_XMALLOC_ARRAY ( char , 8 + NTLMSSP_SIG_SIZE + data_len ) ;
/* Copy the data from the original buffer. */
memcpy ( buf_out + 8 + NTLMSSP_SIG_SIZE , buf + 8 , data_len ) ;
2011-10-20 11:47:53 +04:00
smb_set_enclen ( buf_out , smb_len_nbt ( buf ) + NTLMSSP_SIG_SIZE , enc_ctx_num ) ;
2007-12-27 04:12:36 +03:00
2009-03-24 23:57:01 +03:00
ZERO_STRUCT ( sig ) ;
2007-12-27 04:12:36 +03:00
2011-07-21 13:13:59 +04:00
status = auth_ntlmssp_seal_packet ( auth_ntlmssp_state ,
2010-05-25 14:55:40 +04:00
frame ,
2007-12-27 04:12:36 +03:00
( unsigned char * ) buf_out + 8 + NTLMSSP_SIG_SIZE , /* 4 byte len + 0xFF 'S' <enc> <ctx> */
data_len ,
( unsigned char * ) buf_out + 8 + NTLMSSP_SIG_SIZE ,
data_len ,
& sig ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-05-25 14:55:40 +04:00
talloc_free ( frame ) ;
2007-12-27 04:12:36 +03:00
SAFE_FREE ( buf_out ) ;
return status ;
}
/* First 16 data bytes are signature for SSPI compatibility. */
memcpy ( buf_out + 8 , sig . data , NTLMSSP_SIG_SIZE ) ;
2010-05-25 14:55:40 +04:00
talloc_free ( frame ) ;
2007-12-27 04:12:36 +03:00
* ppbuf_out = buf_out ;
return NT_STATUS_OK ;
}
/******************************************************************************
Generic code for client and server .
gss - api decrypt an incoming buffer . We insist that the size of the
unwrapped buffer must be smaller or identical to the incoming buffer .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
static NTSTATUS common_gss_decrypt_buffer ( struct smb_tran_enc_state_gss * gss_state , char * buf )
{
gss_ctx_id_t gss_ctx = gss_state - > gss_ctx ;
OM_uint32 ret = 0 ;
OM_uint32 minor = 0 ;
int flags_got = 0 ;
gss_buffer_desc in_buf , out_buf ;
2011-10-20 11:47:53 +04:00
size_t buf_len = smb_len_nbt ( buf ) + 4 ; /* Don't forget the 4 length bytes. */
2007-12-27 04:12:36 +03:00
if ( buf_len < 8 ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
in_buf . value = buf + 8 ;
in_buf . length = buf_len - 8 ;
ret = gss_unwrap ( & minor ,
gss_ctx ,
& in_buf ,
& out_buf ,
& flags_got , /* did we get sign+seal ? */
( gss_qop_t * ) NULL ) ;
if ( ret ! = GSS_S_COMPLETE ) {
2011-10-20 15:23:27 +04:00
NTSTATUS status = NT_STATUS_ACCESS_DENIED ;
char * gss_err ;
gss_err = gssapi_error_string ( talloc_tos ( ) ,
ret , minor ,
GSS_C_NULL_OID ) ;
DEBUG ( 0 , ( " common_gss_decrypt_buffer: gss_unwrap failed. "
" Error [%d/%d] - %s - %s \n " ,
ret , minor , nt_errstr ( status ) ,
gss_err ? gss_err : " <unknown> " ) ) ;
talloc_free ( gss_err ) ;
return status ;
2007-12-27 04:12:36 +03:00
}
if ( out_buf . length > in_buf . length ) {
2011-10-20 15:23:27 +04:00
DEBUG ( 0 , ( " common_gss_decrypt_buffer: gss_unwrap size (%u) too large (%u) ! \n " ,
2007-12-27 04:12:36 +03:00
( unsigned int ) out_buf . length ,
( unsigned int ) in_buf . length ) ) ;
gss_release_buffer ( & minor , & out_buf ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
memcpy ( buf + 8 , out_buf . value , out_buf . length ) ;
2008-01-04 23:56:23 +03:00
/* Reset the length and overwrite the header. */
2011-10-20 11:47:53 +04:00
smb_setlen_nbt ( buf , out_buf . length + 4 ) ;
2007-12-27 04:12:36 +03:00
gss_release_buffer ( & minor , & out_buf ) ;
return NT_STATUS_OK ;
}
/******************************************************************************
Generic code for client and server .
gss - api encrypt an outgoing buffer . Return the alloced encrypted pointer in buf_out .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS common_gss_encrypt_buffer ( struct smb_tran_enc_state_gss * gss_state ,
2011-10-19 17:35:23 +04:00
uint16_t enc_ctx_num ,
2007-12-27 04:12:36 +03:00
char * buf ,
char * * ppbuf_out )
{
gss_ctx_id_t gss_ctx = gss_state - > gss_ctx ;
OM_uint32 ret = 0 ;
OM_uint32 minor = 0 ;
int flags_got = 0 ;
gss_buffer_desc in_buf , out_buf ;
2011-10-20 11:47:53 +04:00
size_t buf_len = smb_len_nbt ( buf ) + 4 ; /* Don't forget the 4 length bytes. */
2007-12-27 04:12:36 +03:00
* ppbuf_out = NULL ;
if ( buf_len < 8 ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
in_buf . value = buf + 8 ;
in_buf . length = buf_len - 8 ;
ret = gss_wrap ( & minor ,
gss_ctx ,
true , /* we want sign+seal. */
GSS_C_QOP_DEFAULT ,
& in_buf ,
& flags_got , /* did we get sign+seal ? */
& out_buf ) ;
if ( ret ! = GSS_S_COMPLETE ) {
2011-10-20 15:23:27 +04:00
NTSTATUS status = NT_STATUS_ACCESS_DENIED ;
char * gss_err ;
gss_err = gssapi_error_string ( talloc_tos ( ) ,
ret , minor ,
GSS_C_NULL_OID ) ;
DEBUG ( 0 , ( " common_gss_encrypt_buffer: gss_unwrap failed. "
" Error [%d/%d] - %s - %s \n " ,
ret , minor , nt_errstr ( status ) ,
gss_err ? gss_err : " <unknown> " ) ) ;
talloc_free ( gss_err ) ;
return status ;
2007-12-27 04:12:36 +03:00
}
if ( ! flags_got ) {
/* Sign+seal not supported. */
gss_release_buffer ( & minor , & out_buf ) ;
return NT_STATUS_NOT_SUPPORTED ;
}
/* Ya see - this is why I *hate* gss-api. I don't
* want to have to malloc another buffer of the
* same size + 8 bytes just to get a continuous
* header + buffer , but gss won ' t let me pass in
* a pre - allocated buffer . Bastards ( and you know
* who you are . . . . ) . I might fix this by
* going to " encrypt_and_send " passing in a file
* descriptor and doing scatter - gather write with
* TCP cork on Linux . But I shouldn ' t have to
* bother : - * ( . JRA .
*/
* ppbuf_out = ( char * ) SMB_MALLOC ( out_buf . length + 8 ) ; /* We know this can't wrap. */
if ( ! * ppbuf_out ) {
gss_release_buffer ( & minor , & out_buf ) ;
return NT_STATUS_NO_MEMORY ;
}
memcpy ( * ppbuf_out + 8 , out_buf . value , out_buf . length ) ;
smb_set_enclen ( * ppbuf_out , out_buf . length + 4 , enc_ctx_num ) ;
gss_release_buffer ( & minor , & out_buf ) ;
return NT_STATUS_OK ;
}
# endif
/******************************************************************************
Generic code for client and server .
Encrypt an outgoing buffer . Return the alloced encrypted pointer in buf_out .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS common_encrypt_buffer ( struct smb_trans_enc_state * es , char * buffer , char * * buf_out )
{
if ( ! common_encryption_on ( es ) ) {
/* Not encrypting. */
* buf_out = buffer ;
return NT_STATUS_OK ;
}
switch ( es - > smb_enc_type ) {
case SMB_TRANS_ENC_NTLM :
2011-07-21 13:13:59 +04:00
return common_ntlm_encrypt_buffer ( es - > s . auth_ntlmssp_state , es - > enc_ctx_num , buffer , buf_out ) ;
2007-12-27 04:12:36 +03:00
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
case SMB_TRANS_ENC_GSS :
return common_gss_encrypt_buffer ( es - > s . gss_state , es - > enc_ctx_num , buffer , buf_out ) ;
# endif
default :
return NT_STATUS_NOT_SUPPORTED ;
}
}
/******************************************************************************
Generic code for client and server .
Decrypt an incoming SMB buffer . Replaces the data within it .
New data must be less than or equal to the current length .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS common_decrypt_buffer ( struct smb_trans_enc_state * es , char * buf )
{
if ( ! common_encryption_on ( es ) ) {
/* Not decrypting. */
return NT_STATUS_OK ;
}
switch ( es - > smb_enc_type ) {
case SMB_TRANS_ENC_NTLM :
2011-07-21 13:13:59 +04:00
return common_ntlm_decrypt_buffer ( es - > s . auth_ntlmssp_state , buf ) ;
2007-12-27 04:12:36 +03:00
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
case SMB_TRANS_ENC_GSS :
return common_gss_decrypt_buffer ( es - > s . gss_state , buf ) ;
# endif
default :
return NT_STATUS_NOT_SUPPORTED ;
}
}
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
/******************************************************************************
Shutdown a gss encryption state .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void common_free_gss_state ( struct smb_tran_enc_state_gss * * pp_gss_state )
{
OM_uint32 minor = 0 ;
struct smb_tran_enc_state_gss * gss_state = * pp_gss_state ;
if ( gss_state - > creds ! = GSS_C_NO_CREDENTIAL ) {
gss_release_cred ( & minor , & gss_state - > creds ) ;
}
if ( gss_state - > gss_ctx ! = GSS_C_NO_CONTEXT ) {
gss_delete_sec_context ( & minor , & gss_state - > gss_ctx , NULL ) ;
}
SAFE_FREE ( * pp_gss_state ) ;
}
# endif
/******************************************************************************
Shutdown an encryption state .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void common_free_encryption_state ( struct smb_trans_enc_state * * pp_es )
{
struct smb_trans_enc_state * es = * pp_es ;
if ( es = = NULL ) {
return ;
}
if ( es - > smb_enc_type = = SMB_TRANS_ENC_NTLM ) {
2011-07-21 13:13:59 +04:00
if ( es - > s . auth_ntlmssp_state ) {
TALLOC_FREE ( es - > s . auth_ntlmssp_state ) ;
2007-12-27 04:12:36 +03:00
}
}
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
if ( es - > smb_enc_type = = SMB_TRANS_ENC_GSS ) {
/* Free the gss context handle. */
if ( es - > s . gss_state ) {
common_free_gss_state ( & es - > s . gss_state ) ;
}
}
# endif
SAFE_FREE ( es ) ;
* pp_es = NULL ;
}
/******************************************************************************
Free an encryption - allocated buffer .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void common_free_enc_buffer ( struct smb_trans_enc_state * es , char * buf )
{
2008-12-06 00:20:55 +03:00
uint16_t enc_ctx_num ;
2007-12-27 04:12:36 +03:00
if ( ! common_encryption_on ( es ) ) {
return ;
}
2008-12-06 00:20:55 +03:00
if ( ! NT_STATUS_IS_OK ( get_enc_ctx_num ( ( const uint8_t * ) buf ,
& enc_ctx_num ) ) ) {
return ;
}
2007-12-27 04:12:36 +03:00
if ( es - > smb_enc_type = = SMB_TRANS_ENC_NTLM ) {
SAFE_FREE ( buf ) ;
return ;
}
# if defined(HAVE_GSSAPI) && defined(HAVE_KRB5)
if ( es - > smb_enc_type = = SMB_TRANS_ENC_GSS ) {
OM_uint32 min ;
gss_buffer_desc rel_buf ;
rel_buf . value = buf ;
2011-10-20 11:47:53 +04:00
rel_buf . length = smb_len_nbt ( buf ) + 4 ;
2007-12-27 04:12:36 +03:00
gss_release_buffer ( & min , & rel_buf ) ;
}
# endif
}