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-07-02 02:32:52 +04:00
# include "libads/kerberos_proto.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 ;
}
2010-07-16 22:05:34 +04:00
static NTSTATUS setup_ntlmssp_server_info ( struct smbd_smb2_session * session ,
NTSTATUS status )
{
if ( NT_STATUS_IS_OK ( status ) ) {
2010-07-17 02:33:38 +04:00
status = auth_ntlmssp_steal_server_info ( session ,
2010-07-16 22:05:34 +04:00
session - > auth_ntlmssp_state ,
& session - > server_info ) ;
} else {
/* Note that this server_info won't have a session
* 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 ,
& session - > server_info ,
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 ;
char * client = NULL ;
char * p = NULL ;
char * domain = NULL ;
struct passwd * pw = NULL ;
NTSTATUS status ;
fstring user ;
fstring real_username ;
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 ,
& client , & logon_info , & ap_rep ,
& session_key , true ) ;
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 ;
}
DEBUG ( 3 , ( " smb2: Ticket name is [%s] \n " , client ) ) ;
p = strchr_m ( client , ' @ ' ) ;
if ( ! p ) {
DEBUG ( 3 , ( " smb2: %s Doesn't look like a valid principal \n " ,
client ) ) ;
status = NT_STATUS_LOGON_FAILURE ;
goto fail ;
}
* p = 0 ;
/* save the PAC data if we have it */
if ( logon_info ) {
netsamlogon_cache_store ( client , & logon_info - > info3 ) ;
}
if ( ! strequal ( p + 1 , lp_realm ( ) ) ) {
DEBUG ( 3 , ( " smb2: Ticket for foreign realm %s@%s \n " , client , p + 1 ) ) ;
if ( ! lp_allow_trusted_domains ( ) ) {
status = NT_STATUS_LOGON_FAILURE ;
goto fail ;
}
}
/* this gives a fully qualified user name (ie. with full realm).
that leads to very long usernames , but what else can we do ? */
domain = p + 1 ;
if ( logon_info & & logon_info - > info3 . base . domain . string ) {
domain = talloc_strdup ( talloc_tos ( ) ,
logon_info - > info3 . base . domain . string ) ;
if ( ! domain ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
DEBUG ( 10 , ( " smb2: Mapped to [%s] (using PAC) \n " , domain ) ) ;
} else {
/* If we have winbind running, we can (and must) shorten the
username by using the short netbios name . Otherwise we will
have inconsistent user names . With Kerberos , we get the
fully qualified realm , with ntlmssp we get the short
name . And even w2k3 does use ntlmssp if you for example
connect to an ip address . */
wbcErr wbc_status ;
struct wbcDomainInfo * info = NULL ;
DEBUG ( 10 , ( " smb2: Mapping [%s] to short name \n " , domain ) ) ;
wbc_status = wbcDomainInfo ( domain , & info ) ;
if ( WBC_ERROR_IS_OK ( wbc_status ) ) {
domain = talloc_strdup ( talloc_tos ( ) , info - > short_name ) ;
wbcFreeMemory ( info ) ;
if ( ! domain ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
DEBUG ( 10 , ( " smb2: Mapped to [%s] (using Winbind) \n " , domain ) ) ;
} else {
DEBUG ( 3 , ( " smb2: Could not find short name: %s \n " ,
wbcErrorString ( wbc_status ) ) ) ;
}
}
/* We have to use fstring for this - map_username requires it. */
fstr_sprintf ( user , " %s%c%s " , domain , * lp_winbind_separator ( ) , client ) ;
/* lookup the passwd struct, create a new user if necessary */
2010-05-31 18:52:06 +04:00
username_was_mapped = map_username ( user ) ;
2010-05-18 05:22:19 +04:00
pw = smb_getpwnam ( talloc_tos ( ) , user , real_username , true ) ;
if ( pw ) {
/* if a real user check pam account restrictions */
/* only really perfomed if "obey pam restriction" is true */
/* do this before an eventual mapping to guest occurs */
status = smb_pam_accountcheck ( pw - > pw_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " smb2: PAM account restriction "
" prevents user login \n " ) ) ;
goto fail ;
}
}
if ( ! pw ) {
/* this was originally the behavior of Samba 2.2, if a user
did not have a local uid but has been authenticated , then
map them to a guest account */
if ( lp_map_to_guest ( ) = = MAP_TO_GUEST_ON_BAD_UID ) {
map_domainuser_to_guest = true ;
fstrcpy ( user , lp_guestaccount ( ) ) ;
pw = smb_getpwnam ( talloc_tos ( ) , user , real_username , true ) ;
}
/* extra sanity check that the guest account is valid */
if ( ! pw ) {
DEBUG ( 1 , ( " smb2: Username %s is invalid on this system \n " ,
user ) ) ;
status = NT_STATUS_LOGON_FAILURE ;
goto fail ;
}
}
/* setup the string used by %U */
sub_set_smb_name ( real_username ) ;
reload_services ( true ) ;
if ( map_domainuser_to_guest ) {
make_server_info_guest ( session , & session - > server_info ) ;
} else if ( logon_info ) {
/* pass the unmapped username here since map_username()
will be called again from inside make_server_info_info3 ( ) */
status = make_server_info_info3 ( session ,
client ,
domain ,
& session - > server_info ,
& logon_info - > info3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " smb2: make_server_info_info3 failed: %s! \n " ,
nt_errstr ( status ) ) ) ;
goto fail ;
}
} else {
/*
* We didn ' t get a PAC , we have to make up the user
* ourselves . Try to ask the pdb backend to provide
* SID consistency with ntlmssp session setup
*/
struct samu * sampass ;
/* The stupid make_server_info_XX functions here
don ' t take a talloc context . */
struct auth_serversupplied_info * tmp_server_info = NULL ;
sampass = samu_new ( talloc_tos ( ) ) ;
if ( sampass = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
if ( pdb_getsampwnam ( sampass , real_username ) ) {
DEBUG ( 10 , ( " smb2: found user %s in passdb, calling "
" make_server_info_sam \n " , real_username ) ) ;
status = make_server_info_sam ( & tmp_server_info , sampass ) ;
2010-05-27 13:31:45 +04:00
TALLOC_FREE ( sampass ) ;
2010-05-18 05:22:19 +04:00
} else {
/*
* User not in passdb , make it up artificially
*/
TALLOC_FREE ( sampass ) ;
DEBUG ( 10 , ( " smb2: didn't find user %s in passdb, calling "
" make_server_info_pw \n " , real_username ) ) ;
status = make_server_info_pw ( & tmp_server_info ,
real_username ,
pw ) ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " smb2: make_server_info_[sam|pw] failed: %s! \n " ,
nt_errstr ( status ) ) ) ;
goto fail ;
}
/* Steal tmp_server_info into the session->server_info
pointer . */
session - > server_info = talloc_move ( session , & tmp_server_info ) ;
/* make_server_info_pw does not set the domain. Without this
* we end up with the local netbios name in substitutions for
* % D . */
2010-05-27 11:41:56 +04:00
if ( session - > server_info - > info3 ! = NULL ) {
session - > server_info - > info3 - > base . domain . string =
talloc_strdup ( session - > server_info - > info3 , domain ) ;
2010-05-18 05:22:19 +04:00
}
}
session - > server_info - > nss_token | = username_was_mapped ;
/* we need to build the token for the user. make_server_info_guest()
already does this */
if ( ! session - > server_info - > ptok ) {
status = create_local_token ( session - > server_info ) ;
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 ;
}
if ( session - > server_info - > guest ) {
/* 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 ;
}
data_blob_free ( & session - > server_info - > user_session_key ) ;
session - > server_info - > user_session_key =
data_blob_talloc (
session - > server_info ,
session_key . data ,
session_key . length ) ;
if ( session_key . length > 0 ) {
if ( session - > server_info - > user_session_key . data = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
}
session - > session_key = session - > server_info - > user_session_key ;
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 ;
session - > compat_vuser - > server_info = session - > server_info ;
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 */
alpha_strcpy ( tmp ,
client ,
" . _-$ " ,
sizeof ( tmp ) ) ;
session - > server_info - > sanitized_username = talloc_strdup (
session - > server_info , tmp ) ;
if ( ! session - > server_info - > guest ) {
session - > compat_vuser - > homes_snum =
register_homes_share ( session - > server_info - > unix_name ) ;
}
2010-07-04 20:06:26 +04:00
if ( ! session_claim ( sconn_server_id ( 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 ;
}
2010-05-25 09:34:06 +04:00
if ( session - > server_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 ;
}
session - > session_key = session - > server_info - > user_session_key ;
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 ;
session - > compat_vuser - > server_info = session - > server_info ;
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 ) ) ;
2010-05-18 05:22:19 +04:00
session - > server_info - > sanitized_username = talloc_strdup (
2010-05-25 09:34:06 +04:00
session - > server_info , tmp ) ;
2010-05-18 05:22:19 +04:00
if ( ! session - > compat_vuser - > server_info - > guest ) {
session - > compat_vuser - > homes_snum =
register_homes_share ( session - > server_info - > unix_name ) ;
}
2010-07-04 20:06:26 +04:00
if ( ! session_claim ( sconn_server_id ( 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 ) ;
2010-07-16 22:05:34 +04:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
status = setup_ntlmssp_server_info ( session , status ) ;
}
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
status = setup_ntlmssp_server_info ( session , status ) ;
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 ;
}
2009-05-27 20:33:45 +04:00
set_current_user_info ( session - > server_info - > sanitized_username ,
session - > server_info - > unix_name ,
2010-05-27 11:41:56 +04:00
session - > server_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 ) ;
}