2009-05-15 13:20:34 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2010-05-18 00:05:22 +04:00
Copyright ( C ) Jeremy Allison 2010
2009-05-15 13:20:34 +04:00
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 "smbd/globals.h"
2009-08-12 19:52:55 +04:00
# include "../libcli/smb/smb_common.h"
2009-09-17 02:21:01 +04:00
# include "../libcli/auth/spnego.h"
2010-05-25 14:19:22 +04:00
# include "../libcli/auth/ntlmssp.h"
2010-07-19 21:36:33 +04:00
# include "ntlmssp_wrap.h"
2010-08-03 01:12:16 +04:00
# include "../librpc/gen_ndr/krb5pac.h"
2010-07-02 02:32:52 +04:00
# include "libads/kerberos_proto.h"
2011-02-24 14:27:29 +03:00
# include "../lib/util/asn1.h"
2009-05-15 13:20:34 +04:00
2010-05-18 00:05:22 +04:00
static NTSTATUS smbd_smb2_session_setup ( struct smbd_smb2_request * smb2req ,
2009-05-15 13:20:34 +04:00
uint64_t in_session_id ,
2009-05-23 00:58:39 +04:00
uint8_t in_security_mode ,
2009-05-15 13:20:34 +04:00
DATA_BLOB in_security_buffer ,
2009-05-23 00:58:39 +04:00
uint16_t * out_session_flags ,
2009-05-15 13:20:34 +04:00
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id ) ;
2010-05-18 00:05:22 +04:00
NTSTATUS smbd_smb2_request_process_sesssetup ( struct smbd_smb2_request * smb2req )
2009-05-15 13:20:34 +04:00
{
const uint8_t * inhdr ;
const uint8_t * inbody ;
2010-05-18 00:05:22 +04:00
int i = smb2req - > current_idx ;
2009-05-15 13:20:34 +04:00
uint8_t * outhdr ;
DATA_BLOB outbody ;
DATA_BLOB outdyn ;
size_t expected_body_size = 0x19 ;
size_t body_size ;
uint64_t in_session_id ;
2009-05-23 00:58:39 +04:00
uint8_t in_security_mode ;
2009-05-15 13:20:34 +04:00
uint16_t in_security_offset ;
uint16_t in_security_length ;
DATA_BLOB in_security_buffer ;
2009-05-23 00:58:39 +04:00
uint16_t out_session_flags ;
2009-05-15 13:20:34 +04:00
uint64_t out_session_id ;
uint16_t out_security_offset ;
DATA_BLOB out_security_buffer ;
NTSTATUS status ;
2010-05-18 00:05:22 +04:00
inhdr = ( const uint8_t * ) smb2req - > in . vector [ i + 0 ] . iov_base ;
2009-05-15 13:20:34 +04:00
2010-05-18 00:05:22 +04:00
if ( smb2req - > in . vector [ i + 1 ] . iov_len ! = ( expected_body_size & 0xFFFFFFFE ) ) {
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
2010-05-18 00:05:22 +04:00
inbody = ( const uint8_t * ) smb2req - > in . vector [ i + 1 ] . iov_base ;
2009-05-15 13:20:34 +04:00
body_size = SVAL ( inbody , 0x00 ) ;
if ( body_size ! = expected_body_size ) {
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
in_security_offset = SVAL ( inbody , 0x0C ) ;
in_security_length = SVAL ( inbody , 0x0E ) ;
if ( in_security_offset ! = ( SMB2_HDR_BODY + ( body_size & 0xFFFFFFFE ) ) ) {
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
2010-05-18 00:05:22 +04:00
if ( in_security_length > smb2req - > in . vector [ i + 2 ] . iov_len ) {
return smbd_smb2_request_error ( smb2req , NT_STATUS_INVALID_PARAMETER ) ;
2009-05-15 13:20:34 +04:00
}
2009-05-22 13:06:54 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2009-05-23 00:58:39 +04:00
in_security_mode = CVAL ( inbody , 0x03 ) ;
2010-05-18 00:05:22 +04:00
in_security_buffer . data = ( uint8_t * ) smb2req - > in . vector [ i + 2 ] . iov_base ;
2009-05-15 13:20:34 +04:00
in_security_buffer . length = in_security_length ;
2010-05-18 00:05:22 +04:00
status = smbd_smb2_session_setup ( smb2req ,
2009-05-15 13:20:34 +04:00
in_session_id ,
2009-05-23 00:58:39 +04:00
in_security_mode ,
2009-05-15 13:20:34 +04:00
in_security_buffer ,
2009-05-23 00:58:39 +04:00
& out_session_flags ,
2009-05-15 13:20:34 +04:00
& out_security_buffer ,
& out_session_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
status = nt_status_squash ( status ) ;
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , status ) ;
2009-05-15 13:20:34 +04:00
}
out_security_offset = SMB2_HDR_BODY + 0x08 ;
2010-05-18 00:05:22 +04:00
outhdr = ( uint8_t * ) smb2req - > out . vector [ i ] . iov_base ;
2009-05-15 13:20:34 +04:00
2010-05-18 00:05:22 +04:00
outbody = data_blob_talloc ( smb2req - > out . vector , NULL , 0x08 ) ;
2009-05-15 13:20:34 +04:00
if ( outbody . data = = NULL ) {
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_error ( smb2req , NT_STATUS_NO_MEMORY ) ;
2009-05-15 13:20:34 +04:00
}
SBVAL ( outhdr , SMB2_HDR_SESSION_ID , out_session_id ) ;
SSVAL ( outbody . data , 0x00 , 0x08 + 1 ) ; /* struct size */
2009-05-23 00:58:39 +04:00
SSVAL ( outbody . data , 0x02 ,
out_session_flags ) ; /* session flags */
2009-05-15 13:20:34 +04:00
SSVAL ( outbody . data , 0x04 ,
out_security_offset ) ; /* security buffer offset */
SSVAL ( outbody . data , 0x06 ,
out_security_buffer . length ) ; /* security buffer length */
outdyn = out_security_buffer ;
2010-05-18 00:05:22 +04:00
return smbd_smb2_request_done_ex ( smb2req , status , outbody , & outdyn ,
2009-06-05 13:05:03 +04:00
__location__ ) ;
2009-05-15 13:20:34 +04:00
}
static int smbd_smb2_session_destructor ( struct smbd_smb2_session * session )
{
2009-08-07 17:21:07 +04:00
if ( session - > sconn = = NULL ) {
2009-05-15 13:20:34 +04:00
return 0 ;
}
2009-05-15 13:50:20 +04:00
/* first free all tcons */
while ( session - > tcons . list ) {
talloc_free ( session - > tcons . list ) ;
}
2009-08-07 17:21:07 +04:00
idr_remove ( session - > sconn - > smb2 . sessions . idtree , session - > vuid ) ;
DLIST_REMOVE ( session - > sconn - > smb2 . sessions . list , session ) ;
2009-08-11 20:08:26 +04:00
invalidate_vuid ( session - > sconn , session - > vuid ) ;
2009-05-15 13:20:34 +04:00
2009-05-15 13:40:19 +04:00
session - > vuid = 0 ;
session - > status = NT_STATUS_USER_SESSION_DELETED ;
2009-08-07 17:21:07 +04:00
session - > sconn = NULL ;
2009-05-15 13:40:19 +04:00
2009-05-15 13:20:34 +04:00
return 0 ;
}
2011-02-21 12:25:52 +03:00
static NTSTATUS setup_ntlmssp_session_info ( struct smbd_smb2_session * session ,
2010-07-16 22:05:34 +04:00
NTSTATUS status )
{
if ( NT_STATUS_IS_OK ( status ) ) {
2011-02-21 12:25:52 +03:00
status = auth_ntlmssp_steal_session_info ( session ,
2010-07-16 22:05:34 +04:00
session - > auth_ntlmssp_state ,
2011-02-21 12:25:52 +03:00
& session - > session_info ) ;
2010-07-16 22:05:34 +04:00
} else {
2011-02-21 12:25:52 +03:00
/* Note that this session_info won't have a session
2010-07-16 22:05:34 +04:00
* key . But for map to guest , that ' s exactly the right
* thing - we can ' t reasonably guess the key the
* client wants , as the password was wrong */
status = do_map_to_guest ( status ,
2011-02-21 12:25:52 +03:00
& session - > session_info ,
2010-07-16 22:05:34 +04:00
auth_ntlmssp_get_username ( session - > auth_ntlmssp_state ) ,
auth_ntlmssp_get_domain ( session - > auth_ntlmssp_state ) ) ;
}
return status ;
}
2010-05-20 13:29:03 +04:00
# ifdef HAVE_KRB5
2010-05-18 00:05:22 +04:00
static NTSTATUS smbd_smb2_session_setup_krb5 ( struct smbd_smb2_session * session ,
struct smbd_smb2_request * smb2req ,
2010-05-18 05:22:19 +04:00
uint8_t in_security_mode ,
const DATA_BLOB * secblob ,
const char * mechOID ,
2010-05-18 00:05:22 +04:00
uint16_t * out_session_flags ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
2010-05-18 05:22:19 +04:00
DATA_BLOB ap_rep = data_blob_null ;
DATA_BLOB ap_rep_wrapped = data_blob_null ;
DATA_BLOB ticket = data_blob_null ;
DATA_BLOB session_key = data_blob_null ;
DATA_BLOB secblob_out = data_blob_null ;
uint8 tok_id [ 2 ] ;
struct PAC_LOGON_INFO * logon_info = NULL ;
2010-08-27 01:59:17 +04:00
char * principal = NULL ;
char * user = NULL ;
2010-05-18 05:22:19 +04:00
char * domain = NULL ;
struct passwd * pw = NULL ;
NTSTATUS status ;
2010-08-27 01:59:17 +04:00
char * real_username ;
2010-05-18 05:22:19 +04:00
fstring tmp ;
bool username_was_mapped = false ;
bool map_domainuser_to_guest = false ;
2010-07-21 03:17:58 +04:00
if ( ! spnego_parse_krb5_wrap ( talloc_tos ( ) , * secblob , & ticket , tok_id ) ) {
2010-05-18 05:22:19 +04:00
status = NT_STATUS_LOGON_FAILURE ;
goto fail ;
}
status = ads_verify_ticket ( smb2req , lp_realm ( ) , 0 , & ticket ,
2010-08-27 01:59:17 +04:00
& principal , & logon_info , & ap_rep ,
& session_key , true ) ;
2010-05-18 05:22:19 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " smb2: Failed to verify incoming ticket with error %s! \n " ,
nt_errstr ( status ) ) ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_TIME_DIFFERENCE_AT_DC ) ) {
status = NT_STATUS_LOGON_FAILURE ;
}
goto fail ;
}
2010-08-27 01:59:17 +04:00
status = get_user_from_kerberos_info ( talloc_tos ( ) ,
smb2req - > sconn - > client_id . name ,
principal , logon_info ,
& username_was_mapped ,
& map_domainuser_to_guest ,
& user , & domain ,
& real_username , & pw ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-05-18 05:22:19 +04:00
goto fail ;
}
/* save the PAC data if we have it */
if ( logon_info ) {
2010-08-27 01:59:17 +04:00
netsamlogon_cache_store ( user , & logon_info - > info3 ) ;
2010-05-18 05:22:19 +04:00
}
/* setup the string used by %U */
sub_set_smb_name ( real_username ) ;
2010-08-27 01:59:17 +04:00
/* reload services so that the new %U is taken into account */
2010-08-15 18:13:00 +04:00
reload_services ( smb2req - > sconn - > msg_ctx , smb2req - > sconn - > sock , true ) ;
2010-05-18 05:22:19 +04:00
2010-08-27 02:49:49 +04:00
status = make_server_info_krb5 ( session ,
user , domain , real_username , pw ,
logon_info , map_domainuser_to_guest ,
2011-02-21 12:25:52 +03:00
& session - > session_info ) ;
2010-08-27 02:49:49 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " smb2: make_server_info_krb5 failed \n " ) ) ;
goto fail ;
2010-05-18 05:22:19 +04:00
}
2010-08-27 02:49:49 +04:00
2011-02-21 12:25:52 +03:00
session - > session_info - > nss_token | = username_was_mapped ;
2010-05-18 05:22:19 +04:00
2011-02-21 12:25:52 +03:00
/* we need to build the token for the user. make_session_info_guest()
2010-05-18 05:22:19 +04:00
already does this */
2011-02-21 12:25:52 +03:00
if ( ! session - > session_info - > security_token ) {
status = create_local_token ( session - > session_info ) ;
2010-05-18 05:22:19 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " smb2: failed to create local token: %s \n " ,
nt_errstr ( status ) ) ) ;
goto fail ;
}
}
if ( ( in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED ) | |
lp_server_signing ( ) = = Required ) {
session - > do_signing = true ;
}
2011-02-21 12:25:52 +03:00
if ( session - > session_info - > guest ) {
2010-05-18 05:22:19 +04:00
/* we map anonymous to guest internally */
* out_session_flags | = SMB2_SESSION_FLAG_IS_GUEST ;
* out_session_flags | = SMB2_SESSION_FLAG_IS_NULL ;
/* force no signing */
session - > do_signing = false ;
}
2011-02-21 12:25:52 +03:00
data_blob_free ( & session - > session_info - > user_session_key ) ;
session - > session_info - > user_session_key =
2010-05-18 05:22:19 +04:00
data_blob_talloc (
2011-02-21 12:25:52 +03:00
session - > session_info ,
2010-05-18 05:22:19 +04:00
session_key . data ,
session_key . length ) ;
if ( session_key . length > 0 ) {
2011-02-21 12:25:52 +03:00
if ( session - > session_info - > user_session_key . data = = NULL ) {
2010-05-18 05:22:19 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
}
2011-02-21 12:25:52 +03:00
session - > session_key = session - > session_info - > user_session_key ;
2010-05-18 05:22:19 +04:00
session - > compat_vuser = talloc_zero ( session , user_struct ) ;
if ( session - > compat_vuser = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
session - > compat_vuser - > auth_ntlmssp_state = NULL ;
session - > compat_vuser - > homes_snum = - 1 ;
2011-02-21 12:25:52 +03:00
session - > compat_vuser - > session_info = session - > session_info ;
2010-05-18 05:22:19 +04:00
session - > compat_vuser - > session_keystr = NULL ;
session - > compat_vuser - > vuid = session - > vuid ;
DLIST_ADD ( session - > sconn - > smb1 . sessions . validated_users , session - > compat_vuser ) ;
/* This is a potentially untrusted username */
2010-08-27 01:59:17 +04:00
alpha_strcpy ( tmp , user , " . _-$ " , sizeof ( tmp ) ) ;
2011-02-21 12:25:52 +03:00
session - > session_info - > sanitized_username =
talloc_strdup ( session - > session_info , tmp ) ;
2010-05-18 05:22:19 +04:00
2011-02-21 12:25:52 +03:00
if ( ! session - > session_info - > guest ) {
2010-05-18 05:22:19 +04:00
session - > compat_vuser - > homes_snum =
2011-02-21 12:25:52 +03:00
register_homes_share ( session - > session_info - > unix_name ) ;
2010-05-18 05:22:19 +04:00
}
2010-08-16 10:00:48 +04:00
if ( ! session_claim ( session - > sconn , session - > compat_vuser ) ) {
2010-05-18 05:22:19 +04:00
DEBUG ( 1 , ( " smb2: Failed to claim session "
" for vuid=%d \n " ,
session - > compat_vuser - > vuid ) ) ;
goto fail ;
}
session - > status = NT_STATUS_OK ;
/*
* we attach the session to the request
* so that the response can be signed
*/
smb2req - > session = session ;
if ( session - > do_signing ) {
smb2req - > do_signing = true ;
}
global_client_caps | = ( CAP_LEVEL_II_OPLOCKS | CAP_STATUS32 ) ;
status = NT_STATUS_OK ;
/* wrap that up in a nice GSS-API wrapping */
2010-07-21 03:17:58 +04:00
ap_rep_wrapped = spnego_gen_krb5_wrap ( talloc_tos ( ) , ap_rep ,
2010-05-18 05:22:19 +04:00
TOK_ID_KRB_AP_REP ) ;
secblob_out = spnego_gen_auth_response (
2010-07-21 03:17:58 +04:00
talloc_tos ( ) ,
2010-05-18 05:22:19 +04:00
& ap_rep_wrapped ,
status ,
mechOID ) ;
* out_security_buffer = data_blob_talloc ( smb2req ,
secblob_out . data ,
secblob_out . length ) ;
if ( secblob_out . data & & out_security_buffer - > data = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
data_blob_free ( & ap_rep ) ;
data_blob_free ( & ap_rep_wrapped ) ;
data_blob_free ( & ticket ) ;
data_blob_free ( & session_key ) ;
data_blob_free ( & secblob_out ) ;
* out_session_id = session - > vuid ;
return NT_STATUS_OK ;
fail :
data_blob_free ( & ap_rep ) ;
data_blob_free ( & ap_rep_wrapped ) ;
data_blob_free ( & ticket ) ;
data_blob_free ( & session_key ) ;
data_blob_free ( & secblob_out ) ;
ap_rep_wrapped = data_blob_null ;
secblob_out = spnego_gen_auth_response (
2010-07-21 03:17:58 +04:00
talloc_tos ( ) ,
2010-05-18 05:22:19 +04:00
& ap_rep_wrapped ,
status ,
mechOID ) ;
* out_security_buffer = data_blob_talloc ( smb2req ,
secblob_out . data ,
secblob_out . length ) ;
data_blob_free ( & secblob_out ) ;
return status ;
2010-05-18 00:05:22 +04:00
}
2010-05-20 13:29:03 +04:00
# endif
2010-05-18 00:05:22 +04:00
static NTSTATUS smbd_smb2_spnego_negotiate ( struct smbd_smb2_session * session ,
struct smbd_smb2_request * smb2req ,
2010-05-21 23:08:18 +04:00
uint8_t in_security_mode ,
2009-05-15 13:20:34 +04:00
DATA_BLOB in_security_buffer ,
2009-05-23 00:58:39 +04:00
uint16_t * out_session_flags ,
2009-05-15 13:20:34 +04:00
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
2010-05-18 00:05:22 +04:00
DATA_BLOB secblob_in = data_blob_null ;
DATA_BLOB chal_out = data_blob_null ;
char * kerb_mech = NULL ;
2009-05-15 13:20:34 +04:00
NTSTATUS status ;
2010-05-18 00:05:22 +04:00
/* Ensure we have no old NTLM state around. */
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2009-05-15 13:50:20 +04:00
2010-07-21 00:35:43 +04:00
status = parse_spnego_mechanisms ( talloc_tos ( ) , in_security_buffer ,
2010-05-18 00:05:22 +04:00
& secblob_in , & kerb_mech ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
2009-05-15 13:20:34 +04:00
}
2010-05-18 00:05:22 +04:00
# ifdef HAVE_KRB5
if ( kerb_mech & & ( ( lp_security ( ) = = SEC_ADS ) | |
USE_KERBEROS_KEYTAB ) ) {
status = smbd_smb2_session_setup_krb5 ( session ,
smb2req ,
2010-05-21 23:08:18 +04:00
in_security_mode ,
2010-05-18 00:05:22 +04:00
& secblob_in ,
kerb_mech ,
out_session_flags ,
out_security_buffer ,
out_session_id ) ;
goto out ;
2009-05-15 13:20:34 +04:00
}
2010-05-18 00:05:22 +04:00
# endif
2009-05-15 13:20:34 +04:00
2010-06-03 22:18:11 +04:00
if ( kerb_mech ) {
/* The mechtoken is a krb5 ticket, but
* we need to fall back to NTLM . */
2009-05-15 13:20:34 +04:00
2010-06-03 22:18:11 +04:00
DEBUG ( 3 , ( " smb2: Got krb5 ticket in SPNEGO "
" but set to downgrade to NTLMSSP \n " ) ) ;
status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
} else {
/* Fall back to NTLMSSP. */
status = auth_ntlmssp_start ( & session - > auth_ntlmssp_state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto out ;
}
status = auth_ntlmssp_update ( session - > auth_ntlmssp_state ,
secblob_in ,
& chal_out ) ;
}
2009-06-04 22:14:20 +04:00
2010-05-18 00:05:22 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status ,
NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
goto out ;
}
2009-06-04 22:14:20 +04:00
2010-07-21 03:17:58 +04:00
* out_security_buffer = spnego_gen_auth_response ( smb2req ,
& chal_out ,
2010-05-18 00:05:22 +04:00
status ,
OID_NTLMSSP ) ;
2010-07-21 03:17:58 +04:00
if ( out_security_buffer - > data = = NULL ) {
2010-05-18 00:05:22 +04:00
status = NT_STATUS_NO_MEMORY ;
goto out ;
}
* out_session_id = session - > vuid ;
2009-06-04 22:14:20 +04:00
2010-05-18 00:05:22 +04:00
out :
2009-06-04 22:14:20 +04:00
2010-05-18 00:05:22 +04:00
data_blob_free ( & secblob_in ) ;
data_blob_free ( & chal_out ) ;
2010-07-21 00:35:43 +04:00
TALLOC_FREE ( kerb_mech ) ;
2010-05-18 00:05:22 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status ,
NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
2009-05-23 00:58:39 +04:00
}
2010-05-18 00:05:22 +04:00
return status ;
}
2009-05-23 00:58:39 +04:00
2010-05-18 00:05:22 +04:00
static NTSTATUS smbd_smb2_common_ntlmssp_auth_return ( struct smbd_smb2_session * session ,
struct smbd_smb2_request * smb2req ,
uint8_t in_security_mode ,
DATA_BLOB in_security_buffer ,
2010-05-18 05:22:19 +04:00
uint16_t * out_session_flags ,
uint64_t * out_session_id )
2010-05-18 00:05:22 +04:00
{
2010-05-18 05:22:19 +04:00
fstring tmp ;
2009-05-23 00:58:39 +04:00
if ( ( in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED ) | |
lp_server_signing ( ) = = Required ) {
session - > do_signing = true ;
}
2011-02-21 12:25:52 +03:00
if ( session - > session_info - > guest ) {
2009-05-23 00:58:39 +04:00
/* we map anonymous to guest internally */
* out_session_flags | = SMB2_SESSION_FLAG_IS_GUEST ;
* out_session_flags | = SMB2_SESSION_FLAG_IS_NULL ;
/* force no signing */
session - > do_signing = false ;
}
2011-02-21 12:25:52 +03:00
session - > session_key = session - > session_info - > user_session_key ;
2009-05-23 00:58:39 +04:00
2009-05-27 20:33:45 +04:00
session - > compat_vuser = talloc_zero ( session , user_struct ) ;
if ( session - > compat_vuser = = NULL ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2009-05-27 20:33:45 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_NO_MEMORY ;
}
session - > compat_vuser - > auth_ntlmssp_state = session - > auth_ntlmssp_state ;
session - > compat_vuser - > homes_snum = - 1 ;
2011-02-21 12:25:52 +03:00
session - > compat_vuser - > session_info = session - > session_info ;
2009-05-27 20:33:45 +04:00
session - > compat_vuser - > session_keystr = NULL ;
session - > compat_vuser - > vuid = session - > vuid ;
2009-08-07 17:21:07 +04:00
DLIST_ADD ( session - > sconn - > smb1 . sessions . validated_users , session - > compat_vuser ) ;
2009-05-27 20:33:45 +04:00
2010-05-18 05:22:19 +04:00
/* This is a potentially untrusted username */
alpha_strcpy ( tmp ,
2010-05-25 09:34:06 +04:00
auth_ntlmssp_get_username ( session - > auth_ntlmssp_state ) ,
" . _-$ " ,
sizeof ( tmp ) ) ;
2011-02-21 12:25:52 +03:00
session - > session_info - > sanitized_username = talloc_strdup (
session - > session_info , tmp ) ;
2010-05-18 05:22:19 +04:00
2011-02-21 12:25:52 +03:00
if ( ! session - > compat_vuser - > session_info - > guest ) {
2010-05-18 05:22:19 +04:00
session - > compat_vuser - > homes_snum =
2011-02-21 12:25:52 +03:00
register_homes_share ( session - > session_info - > unix_name ) ;
2010-05-18 05:22:19 +04:00
}
2010-08-16 10:00:48 +04:00
if ( ! session_claim ( session - > sconn , session - > compat_vuser ) ) {
2010-05-18 05:22:19 +04:00
DEBUG ( 1 , ( " smb2: Failed to claim session "
" for vuid=%d \n " ,
session - > compat_vuser - > vuid ) ) ;
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 05:22:19 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_LOGON_FAILURE ;
}
2009-05-23 00:58:39 +04:00
session - > status = NT_STATUS_OK ;
/*
* we attach the session to the request
* so that the response can be signed
*/
2010-05-18 00:05:22 +04:00
smb2req - > session = session ;
2009-05-23 00:58:39 +04:00
if ( session - > do_signing ) {
2010-05-18 00:05:22 +04:00
smb2req - > do_signing = true ;
}
global_client_caps | = ( CAP_LEVEL_II_OPLOCKS | CAP_STATUS32 ) ;
2010-05-18 05:22:19 +04:00
* out_session_id = session - > vuid ;
2010-05-18 00:05:22 +04:00
return NT_STATUS_OK ;
}
static NTSTATUS smbd_smb2_spnego_auth ( struct smbd_smb2_session * session ,
struct smbd_smb2_request * smb2req ,
uint8_t in_security_mode ,
DATA_BLOB in_security_buffer ,
uint16_t * out_session_flags ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
DATA_BLOB auth = data_blob_null ;
DATA_BLOB auth_out = data_blob_null ;
NTSTATUS status ;
2010-07-21 03:17:58 +04:00
if ( ! spnego_parse_auth ( talloc_tos ( ) , in_security_buffer , & auth ) ) {
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_LOGON_FAILURE ;
}
2010-05-21 23:08:18 +04:00
if ( auth . data [ 0 ] = = ASN1_APPLICATION ( 0 ) ) {
/* Might be a second negTokenTarg packet */
DATA_BLOB secblob_in = data_blob_null ;
char * kerb_mech = NULL ;
2010-07-21 00:35:43 +04:00
status = parse_spnego_mechanisms ( talloc_tos ( ) ,
in_security_buffer ,
2010-05-21 23:08:18 +04:00
& secblob_in , & kerb_mech ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( session ) ;
return status ;
}
# ifdef HAVE_KRB5
if ( kerb_mech & & ( ( lp_security ( ) = = SEC_ADS ) | |
USE_KERBEROS_KEYTAB ) ) {
status = smbd_smb2_session_setup_krb5 ( session ,
smb2req ,
in_security_mode ,
& secblob_in ,
kerb_mech ,
out_session_flags ,
out_security_buffer ,
out_session_id ) ;
data_blob_free ( & secblob_in ) ;
2010-07-21 00:35:43 +04:00
TALLOC_FREE ( kerb_mech ) ;
2010-05-21 23:08:18 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( session ) ;
}
return status ;
}
# endif
/* Can't blunder into NTLMSSP auth if we have
* a krb5 ticket . */
if ( kerb_mech ) {
DEBUG ( 3 , ( " smb2: network "
" misconfiguration, client sent us a "
" krb5 ticket and kerberos security "
" not enabled \n " ) ) ;
TALLOC_FREE ( session ) ;
data_blob_free ( & secblob_in ) ;
2010-07-21 00:35:43 +04:00
TALLOC_FREE ( kerb_mech ) ;
2010-05-21 23:08:18 +04:00
return NT_STATUS_LOGON_FAILURE ;
}
2010-06-03 22:18:11 +04:00
data_blob_free ( & secblob_in ) ;
}
if ( session - > auth_ntlmssp_state = = NULL ) {
status = auth_ntlmssp_start ( & session - > auth_ntlmssp_state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
data_blob_free ( & auth ) ;
TALLOC_FREE ( session ) ;
return status ;
}
2010-05-21 23:08:18 +04:00
}
2010-05-18 00:05:22 +04:00
status = auth_ntlmssp_update ( session - > auth_ntlmssp_state ,
auth ,
& auth_out ) ;
2011-02-21 12:25:52 +03:00
/* We need to call setup_ntlmssp_session_info() if status==NT_STATUS_OK,
2010-08-10 03:16:24 +04:00
or if status is anything except NT_STATUS_MORE_PROCESSING_REQUIRED ,
as this can trigger map to guest . */
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2011-02-21 12:25:52 +03:00
status = setup_ntlmssp_session_info ( session , status ) ;
2010-07-16 22:05:34 +04:00
}
2010-06-03 22:50:08 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
data_blob_free ( & auth ) ;
TALLOC_FREE ( session ) ;
return status ;
}
data_blob_free ( & auth ) ;
2010-07-21 03:17:58 +04:00
* out_security_buffer = spnego_gen_auth_response ( smb2req ,
& auth_out , status , NULL ) ;
2010-05-18 00:05:22 +04:00
2010-07-21 03:17:58 +04:00
if ( out_security_buffer - > data = = NULL ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_NO_MEMORY ;
2009-05-15 13:20:34 +04:00
}
* out_session_id = session - > vuid ;
2010-05-11 01:23:44 +04:00
2010-06-03 22:50:08 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
return NT_STATUS_MORE_PROCESSING_REQUIRED ;
}
/* We're done - claim the session. */
2010-05-18 00:05:22 +04:00
return smbd_smb2_common_ntlmssp_auth_return ( session ,
smb2req ,
in_security_mode ,
in_security_buffer ,
2010-05-18 05:22:19 +04:00
out_session_flags ,
out_session_id ) ;
2010-05-18 00:05:22 +04:00
}
static NTSTATUS smbd_smb2_raw_ntlmssp_auth ( struct smbd_smb2_session * session ,
struct smbd_smb2_request * smb2req ,
uint8_t in_security_mode ,
DATA_BLOB in_security_buffer ,
uint16_t * out_session_flags ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
NTSTATUS status ;
DATA_BLOB secblob_out = data_blob_null ;
if ( session - > auth_ntlmssp_state = = NULL ) {
status = auth_ntlmssp_start ( & session - > auth_ntlmssp_state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( session ) ;
return status ;
}
}
/* RAW NTLMSSP */
status = auth_ntlmssp_update ( session - > auth_ntlmssp_state ,
in_security_buffer ,
& secblob_out ) ;
if ( NT_STATUS_IS_OK ( status ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
* out_security_buffer = data_blob_talloc ( smb2req ,
secblob_out . data ,
secblob_out . length ) ;
2010-05-18 05:22:19 +04:00
if ( secblob_out . data & & out_security_buffer - > data = = NULL ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_NO_MEMORY ;
}
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
* out_session_id = session - > vuid ;
return status ;
}
2010-07-16 22:05:34 +04:00
2011-02-21 12:25:52 +03:00
status = setup_ntlmssp_session_info ( session , status ) ;
2010-07-16 22:05:34 +04:00
2010-05-18 00:05:22 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
return status ;
}
* out_session_id = session - > vuid ;
return smbd_smb2_common_ntlmssp_auth_return ( session ,
smb2req ,
in_security_mode ,
in_security_buffer ,
2010-05-18 05:22:19 +04:00
out_session_flags ,
out_session_id ) ;
2010-05-18 00:05:22 +04:00
}
static NTSTATUS smbd_smb2_session_setup ( struct smbd_smb2_request * smb2req ,
uint64_t in_session_id ,
uint8_t in_security_mode ,
DATA_BLOB in_security_buffer ,
uint16_t * out_session_flags ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
struct smbd_smb2_session * session ;
* out_session_flags = 0 ;
* out_session_id = 0 ;
if ( in_session_id = = 0 ) {
int id ;
/* create a new session */
session = talloc_zero ( smb2req - > sconn , struct smbd_smb2_session ) ;
if ( session = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
session - > status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
id = idr_get_new_random ( smb2req - > sconn - > smb2 . sessions . idtree ,
session ,
smb2req - > sconn - > smb2 . sessions . limit ) ;
if ( id = = - 1 ) {
return NT_STATUS_INSUFFICIENT_RESOURCES ;
}
session - > vuid = id ;
session - > tcons . idtree = idr_init ( session ) ;
if ( session - > tcons . idtree = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
session - > tcons . limit = 0x0000FFFE ;
session - > tcons . list = NULL ;
DLIST_ADD_END ( smb2req - > sconn - > smb2 . sessions . list , session ,
struct smbd_smb2_session * ) ;
session - > sconn = smb2req - > sconn ;
talloc_set_destructor ( session , smbd_smb2_session_destructor ) ;
} else {
void * p ;
/* lookup an existing session */
p = idr_find ( smb2req - > sconn - > smb2 . sessions . idtree , in_session_id ) ;
if ( p = = NULL ) {
return NT_STATUS_USER_SESSION_DELETED ;
}
session = talloc_get_type_abort ( p , struct smbd_smb2_session ) ;
}
if ( NT_STATUS_IS_OK ( session - > status ) ) {
return NT_STATUS_REQUEST_NOT_ACCEPTED ;
}
if ( in_security_buffer . data [ 0 ] = = ASN1_APPLICATION ( 0 ) ) {
return smbd_smb2_spnego_negotiate ( session ,
smb2req ,
2010-05-18 05:22:19 +04:00
in_security_mode ,
2010-05-18 00:05:22 +04:00
in_security_buffer ,
out_session_flags ,
out_security_buffer ,
out_session_id ) ;
} else if ( in_security_buffer . data [ 0 ] = = ASN1_CONTEXT ( 1 ) ) {
return smbd_smb2_spnego_auth ( session ,
smb2req ,
in_security_mode ,
in_security_buffer ,
out_session_flags ,
out_security_buffer ,
out_session_id ) ;
} else if ( strncmp ( ( char * ) ( in_security_buffer . data ) , " NTLMSSP " , 7 ) = = 0 ) {
return smbd_smb2_raw_ntlmssp_auth ( session ,
smb2req ,
in_security_mode ,
in_security_buffer ,
out_session_flags ,
out_security_buffer ,
out_session_id ) ;
}
/* Unknown packet type. */
DEBUG ( 1 , ( " Unknown packet type %u in smb2 sessionsetup \n " ,
( unsigned int ) in_security_buffer . data [ 0 ] ) ) ;
2010-07-17 03:44:22 +04:00
TALLOC_FREE ( session - > auth_ntlmssp_state ) ;
2010-05-18 00:05:22 +04:00
TALLOC_FREE ( session ) ;
return NT_STATUS_LOGON_FAILURE ;
2009-05-15 13:20:34 +04:00
}
2009-05-20 22:51:10 +04:00
NTSTATUS smbd_smb2_request_check_session ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
2010-04-07 21:32:01 +04:00
const uint8_t * outhdr ;
2009-05-20 22:51:10 +04:00
int i = req - > current_idx ;
uint64_t in_session_id ;
void * p ;
struct smbd_smb2_session * session ;
2010-04-07 21:32:01 +04:00
bool chained_fixup = false ;
2009-05-20 22:51:10 +04:00
inhdr = ( const uint8_t * ) req - > in . vector [ i + 0 ] . iov_base ;
2009-05-22 13:06:54 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2009-05-20 22:51:10 +04:00
2010-04-18 08:20:17 +04:00
if ( in_session_id = = ( 0xFFFFFFFFFFFFFFFFLL ) ) {
if ( req - > async ) {
/*
* async request - fill in session_id from
* already setup request out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
in_session_id = BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ;
} else if ( i > 2 ) {
/*
* Chained request - fill in session_id from
* the previous request out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i - 3 ] . iov_base ;
in_session_id = BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ;
chained_fixup = true ;
}
2010-04-07 21:32:01 +04:00
}
2009-05-20 22:51:10 +04:00
/* lookup an existing session */
2009-08-07 17:21:07 +04:00
p = idr_find ( req - > sconn - > smb2 . sessions . idtree , in_session_id ) ;
2009-05-20 22:51:10 +04:00
if ( p = = NULL ) {
return NT_STATUS_USER_SESSION_DELETED ;
}
session = talloc_get_type_abort ( p , struct smbd_smb2_session ) ;
if ( ! NT_STATUS_IS_OK ( session - > status ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2011-02-21 12:25:52 +03:00
set_current_user_info ( session - > session_info - > sanitized_username ,
session - > session_info - > unix_name ,
session - > session_info - > info3 - > base . domain . string ) ;
2009-05-27 20:33:45 +04:00
2009-05-20 22:51:10 +04:00
req - > session = session ;
2010-04-07 21:32:01 +04:00
if ( chained_fixup ) {
/* Fix up our own outhdr. */
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
SBVAL ( outhdr , SMB2_HDR_SESSION_ID , in_session_id ) ;
}
2009-05-20 22:51:10 +04:00
return NT_STATUS_OK ;
}
2009-05-15 13:40:19 +04:00
NTSTATUS smbd_smb2_request_process_logoff ( struct smbd_smb2_request * req )
{
const uint8_t * inbody ;
int i = req - > current_idx ;
DATA_BLOB outbody ;
size_t expected_body_size = 0x04 ;
size_t body_size ;
if ( req - > in . vector [ i + 1 ] . iov_len ! = ( expected_body_size & 0xFFFFFFFE ) ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
inbody = ( const uint8_t * ) req - > in . vector [ i + 1 ] . iov_base ;
body_size = SVAL ( inbody , 0x00 ) ;
if ( body_size ! = expected_body_size ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
/*
* TODO : cancel all outstanding requests on the session
* and delete all tree connections .
*/
smbd_smb2_session_destructor ( req - > session ) ;
/*
* we may need to sign the response , so we need to keep
* the session until the response is sent to the wire .
*/
talloc_steal ( req , req - > session ) ;
outbody = data_blob_talloc ( req - > out . vector , NULL , 0x04 ) ;
if ( outbody . data = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
SSVAL ( outbody . data , 0x00 , 0x04 ) ; /* struct size */
SSVAL ( outbody . data , 0x02 , 0 ) ; /* reserved */
return smbd_smb2_request_done ( req , outbody , NULL ) ;
}