2003-08-13 01:53:07 +00:00
/*
Unix SMB / CIFS implementation .
Password and authentication handling
2009-02-13 14:02:49 +11:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2001 - 2009
2004-05-13 23:16:33 +00:00
Copyright ( C ) Gerald Carter 2003
2005-01-09 12:55:25 +00:00
Copyright ( C ) Stefan Metzmacher 2005
2003-08-13 01:53:07 +00: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
2007-07-10 02:07:03 +00:00
the Free Software Foundation ; either version 3 of the License , or
2003-08-13 01:53:07 +00:00
( 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
2007-07-10 02:07:03 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2003-08-13 01:53:07 +00:00
*/
# include "includes.h"
2004-11-02 00:24:21 +00:00
# include "system/time.h"
2011-02-10 14:12:51 +11:00
# include <ldb.h>
2010-04-15 11:58:05 +10:00
# include "libcli/ldap/ldap_ndr.h"
# include "libcli/security/security.h"
2006-04-27 19:50:13 +00:00
# include "auth/auth.h"
2009-03-17 14:03:02 +11:00
# include "../libcli/auth/ntlm_check.h"
2008-05-05 15:23:57 +10:00
# include "auth/ntlm/auth_proto.h"
2006-04-27 19:50:13 +00:00
# include "auth/auth_sam.h"
2006-03-14 15:03:25 +00:00
# include "dsdb/samdb/samdb.h"
2010-02-16 15:40:44 +11:00
# include "dsdb/common/util.h"
2007-09-08 12:42:09 +00:00
# include "param/param.h"
2010-09-12 10:06:39 +10:00
# include "librpc/gen_ndr/ndr_irpc_c.h"
2017-04-11 15:51:50 +12:00
# include "librpc/gen_ndr/ndr_winbind_c.h"
2010-09-12 10:06:39 +10:00
# include "lib/messaging/irpc.h"
2013-11-11 10:38:03 +13:00
# include "libcli/auth/libcli_auth.h"
2015-12-28 19:01:54 +00:00
# include "libds/common/roles.h"
2021-04-15 10:04:21 +02:00
# include "lib/util/tevent_ntstatus.h"
2003-08-13 01:53:07 +00:00
2018-05-21 13:51:16 +12:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_AUTH
2011-03-19 00:44:28 +01:00
NTSTATUS auth_sam_init ( void ) ;
2006-04-27 19:50:13 +00:00
extern const char * user_attrs [ ] ;
extern const char * domain_ref_attrs [ ] ;
2003-08-13 01:53:07 +00:00
2006-04-27 19:50:13 +00:00
/****************************************************************************
Do a specific test for an smb password being correct , given a smb_password and
the lanman and NT responses .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-05-07 08:14:06 +02:00
static NTSTATUS authsam_password_ok ( struct auth4_context * auth_context ,
2006-04-27 19:50:13 +00:00
TALLOC_CTX * mem_ctx ,
uint16_t acct_flags ,
const struct samr_Password * lm_pwd ,
const struct samr_Password * nt_pwd ,
const struct auth_usersupplied_info * user_info ,
DATA_BLOB * user_sess_key ,
DATA_BLOB * lm_sess_key )
{
NTSTATUS status ;
switch ( user_info - > password_state ) {
case AUTH_PASSWORD_PLAIN :
{
const struct auth_usersupplied_info * user_info_temp ;
status = encrypt_user_info ( mem_ctx , auth_context ,
AUTH_PASSWORD_HASH ,
user_info , & user_info_temp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Failed to convert plaintext password to password HASH: %s \n " , nt_errstr ( status ) ) ) ;
return status ;
}
user_info = user_info_temp ;
2017-07-26 17:30:53 +02:00
FALL_THROUGH ;
2006-04-27 19:50:13 +00:00
}
case AUTH_PASSWORD_HASH :
* lm_sess_key = data_blob ( NULL , 0 ) ;
* user_sess_key = data_blob ( NULL , 0 ) ;
status = hash_password_check ( mem_ctx ,
2010-07-16 14:32:42 +10:00
lpcfg_lanman_auth ( auth_context - > lp_ctx ) ,
2006-04-27 19:50:13 +00:00
user_info - > password . hash . lanman ,
user_info - > password . hash . nt ,
user_info - > mapped . account_name ,
lm_pwd , nt_pwd ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
break ;
case AUTH_PASSWORD_RESPONSE :
2007-12-02 17:56:09 +01:00
status = ntlm_password_check ( mem_ctx ,
2010-07-16 14:32:42 +10:00
lpcfg_lanman_auth ( auth_context - > lp_ctx ) ,
lpcfg_ntlm_auth ( auth_context - > lp_ctx ) ,
2007-12-02 17:56:09 +01:00
user_info - > logon_parameters ,
2006-04-27 19:50:13 +00:00
& auth_context - > challenge . data ,
& user_info - > password . response . lanman ,
& user_info - > password . response . nt ,
user_info - > mapped . account_name ,
user_info - > client . account_name ,
user_info - > client . domain_name ,
lm_pwd , nt_pwd ,
user_sess_key , lm_sess_key ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
break ;
}
return NT_STATUS_OK ;
}
2017-04-11 15:51:50 +12:00
static void auth_sam_trigger_zero_password ( TALLOC_CTX * mem_ctx ,
struct imessaging_context * msg_ctx ,
struct tevent_context * event_ctx ,
struct netr_SendToSamBase * send_to_sam )
{
struct dcerpc_binding_handle * irpc_handle ;
struct winbind_SendToSam r ;
struct tevent_req * req ;
TALLOC_CTX * tmp_ctx ;
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return ;
}
irpc_handle = irpc_binding_handle_by_name ( tmp_ctx , msg_ctx ,
" winbind_server " ,
& ndr_table_winbind ) ;
if ( irpc_handle = = NULL ) {
DEBUG ( 1 , ( __location__ " : Unable to get binding handle for winbind \n " ) ) ;
TALLOC_FREE ( tmp_ctx ) ;
return ;
}
r . in . message = * send_to_sam ;
/*
* This seem to rely on the current IRPC implementation ,
* which delivers the message in the _send function .
*
* TODO : we need a ONE_WAY IRPC handle and register
* a callback and wait for it to be triggered !
*/
req = dcerpc_winbind_SendToSam_r_send ( tmp_ctx ,
event_ctx ,
irpc_handle ,
& r ) ;
/* we aren't interested in a reply */
talloc_free ( req ) ;
TALLOC_FREE ( tmp_ctx ) ;
}
2006-04-27 19:50:13 +00:00
2010-09-12 10:06:39 +10:00
/*
send a message to the drepl server telling it to initiate a
REPL_SECRET getncchanges extended op to fetch the users secrets
*/
2017-04-03 15:49:45 +12:00
static void auth_sam_trigger_repl_secret ( TALLOC_CTX * mem_ctx ,
struct imessaging_context * msg_ctx ,
struct tevent_context * event_ctx ,
2010-09-12 10:06:39 +10:00
struct ldb_dn * user_dn )
{
struct dcerpc_binding_handle * irpc_handle ;
struct drepl_trigger_repl_secret r ;
struct tevent_req * req ;
2013-11-11 10:32:58 +13:00
TALLOC_CTX * tmp_ctx ;
2010-09-12 10:06:39 +10:00
2017-04-03 15:49:45 +12:00
tmp_ctx = talloc_new ( mem_ctx ) ;
2013-11-11 10:32:58 +13:00
if ( tmp_ctx = = NULL ) {
return ;
}
2017-04-03 15:49:45 +12:00
irpc_handle = irpc_binding_handle_by_name ( tmp_ctx , msg_ctx ,
2010-09-12 10:06:39 +10:00
" dreplsrv " ,
& ndr_table_irpc ) ;
if ( irpc_handle = = NULL ) {
DEBUG ( 1 , ( __location__ " : Unable to get binding handle for dreplsrv \n " ) ) ;
2013-11-11 10:32:58 +13:00
TALLOC_FREE ( tmp_ctx ) ;
2010-09-12 10:06:39 +10:00
return ;
}
r . in . user_dn = ldb_dn_get_linearized ( user_dn ) ;
2013-11-11 10:32:58 +13:00
/*
* This seem to rely on the current IRPC implementation ,
* which delivers the message in the _send function .
*
* TODO : we need a ONE_WAY IRPC handle and register
* a callback and wait for it to be triggered !
*/
req = dcerpc_drepl_trigger_repl_secret_r_send ( tmp_ctx ,
2017-04-03 15:49:45 +12:00
event_ctx ,
2010-09-12 10:06:39 +10:00
irpc_handle ,
& r ) ;
/* we aren't interested in a reply */
talloc_free ( req ) ;
2013-11-11 10:32:58 +13:00
TALLOC_FREE ( tmp_ctx ) ;
2010-09-12 10:06:39 +10:00
}
2006-04-27 19:50:13 +00:00
2013-11-11 10:38:03 +13:00
/*
* Check that a password is OK , and update badPwdCount if required .
*/
static NTSTATUS authsam_password_check_and_record ( struct auth4_context * auth_context ,
TALLOC_CTX * mem_ctx ,
struct ldb_dn * domain_dn ,
struct ldb_message * msg ,
uint16_t acct_flags ,
const struct auth_usersupplied_info * user_info ,
DATA_BLOB * user_sess_key ,
2017-04-03 15:21:29 +12:00
DATA_BLOB * lm_sess_key ,
bool * authoritative )
2004-10-25 04:25:29 +00:00
{
NTSTATUS nt_status ;
2013-11-11 10:38:03 +13:00
NTSTATUS auth_status ;
2013-11-11 10:32:58 +13:00
TALLOC_CTX * tmp_ctx ;
2013-11-11 10:38:03 +13:00
int i , ret ;
int history_len = 0 ;
struct ldb_context * sam_ctx = auth_context - > sam_ctx ;
const char * const attrs [ ] = { " pwdHistoryLength " , NULL } ;
struct ldb_message * dom_msg ;
struct samr_Password * lm_pwd ;
struct samr_Password * nt_pwd ;
2017-04-03 15:22:08 +12:00
bool am_rodc ;
2004-05-02 08:45:00 +00:00
2013-11-11 10:32:58 +13:00
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( tmp_ctx = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2013-11-11 10:38:03 +13:00
/*
* This call does more than what it appears to do , it also
* checks for the account lockout .
*
* It is done here so that all parts of Samba that read the
* password refuse to even operate on it if the account is
* locked out , to avoid mistakes like CVE - 2013 - 4496.
*/
nt_status = samdb_result_passwords ( tmp_ctx , auth_context - > lp_ctx ,
msg , & lm_pwd , & nt_pwd ) ;
2013-11-11 10:32:58 +13:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return nt_status ;
}
2004-05-13 15:34:56 +00:00
2010-09-12 10:06:39 +10:00
if ( lm_pwd = = NULL & & nt_pwd = = NULL ) {
if ( samdb_rodc ( auth_context - > sam_ctx , & am_rodc ) = = LDB_SUCCESS & & am_rodc ) {
2013-11-11 10:38:03 +13:00
/*
* we don ' t have passwords for this
2010-09-12 10:06:39 +10:00
* account . We are an RODC , and this account
* may be one for which we either are denied
* REPL_SECRET replication or we haven ' t yet
* done the replication . We return
* NT_STATUS_NOT_IMPLEMENTED which tells the
* auth code to try the next authentication
* mechanism . We also send a message to our
* drepl server to tell it to try and
* replicate the secrets for this account .
2017-03-23 09:37:22 +01:00
*
* TODO : Should we only trigger this is detected
* there ' s a chance that the password might be
* replicated , we should be able to detect this
* based on msDS - NeverRevealGroup .
2010-09-12 10:06:39 +10:00
*/
2017-04-03 15:49:45 +12:00
auth_sam_trigger_repl_secret ( auth_context ,
auth_context - > msg_ctx ,
auth_context - > event_ctx ,
msg - > dn ) ;
2013-11-11 10:32:58 +13:00
TALLOC_FREE ( tmp_ctx ) ;
2010-09-12 10:06:39 +10:00
return NT_STATUS_NOT_IMPLEMENTED ;
}
}
2013-11-11 10:38:03 +13:00
auth_status = authsam_password_ok ( auth_context , tmp_ctx ,
acct_flags ,
lm_pwd , nt_pwd ,
user_info ,
user_sess_key , lm_sess_key ) ;
if ( NT_STATUS_IS_OK ( auth_status ) ) {
if ( user_sess_key - > data ) {
talloc_steal ( mem_ctx , user_sess_key - > data ) ;
}
if ( lm_sess_key - > data ) {
talloc_steal ( mem_ctx , lm_sess_key - > data ) ;
}
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_OK ;
}
* user_sess_key = data_blob_null ;
* lm_sess_key = data_blob_null ;
if ( ! NT_STATUS_EQUAL ( auth_status , NT_STATUS_WRONG_PASSWORD ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return auth_status ;
}
/*
* We only continue if this was a wrong password
* and we ' ll always return NT_STATUS_WRONG_PASSWORD
* no matter what error happens .
*/
/* pull the domain password property attributes */
ret = dsdb_search_one ( sam_ctx , tmp_ctx , & dom_msg , domain_dn , LDB_SCOPE_BASE ,
attrs , 0 , " objectClass=domain " ) ;
if ( ret = = LDB_SUCCESS ) {
history_len = ldb_msg_find_attr_as_uint ( dom_msg , " pwdHistoryLength " , 0 ) ;
} else if ( ret = = LDB_ERR_NO_SUCH_OBJECT ) {
DEBUG ( 3 , ( " Couldn't find domain %s: %s! \n " ,
ldb_dn_get_linearized ( domain_dn ) ,
ldb_errstring ( sam_ctx ) ) ) ;
} else {
DEBUG ( 3 , ( " error finding domain %s: %s! \n " ,
ldb_dn_get_linearized ( domain_dn ) ,
ldb_errstring ( sam_ctx ) ) ) ;
}
for ( i = 1 ; i < MIN ( history_len , 3 ) ; i + + ) {
struct samr_Password zero_string_hash ;
struct samr_Password zero_string_des_hash ;
struct samr_Password * nt_history_pwd = NULL ;
struct samr_Password * lm_history_pwd = NULL ;
NTTIME pwdLastSet ;
2015-10-22 09:45:26 +13:00
struct timeval tv_now ;
2013-11-11 10:38:03 +13:00
NTTIME now ;
int allowed_period_mins ;
NTTIME allowed_period ;
nt_status = samdb_result_passwords_from_history ( tmp_ctx ,
auth_context - > lp_ctx ,
msg , i ,
& lm_history_pwd ,
& nt_history_pwd ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
/*
* If we don ' t find element ' i ' we won ' t find
* ' i + 1 ' . . .
*/
break ;
}
/*
* We choose to avoid any issues
* around different LM and NT history
* lengths by only checking the NT
* history
*/
if ( nt_history_pwd = = NULL ) {
/*
* If we don ' t find element ' i ' we won ' t find
* ' i + 1 ' . . .
*/
break ;
}
/* Skip over all-zero hashes in the history */
2016-12-31 12:45:51 +00:00
if ( all_zero ( nt_history_pwd - > hash ,
sizeof ( nt_history_pwd - > hash ) ) ) {
2013-11-11 10:38:03 +13:00
continue ;
}
/*
* This looks odd , but the password_hash module writes this in if
* ( somehow ) we didn ' t have an old NT hash
*/
E_md4hash ( " " , zero_string_hash . hash ) ;
if ( memcmp ( nt_history_pwd - > hash , zero_string_hash . hash , 16 ) = = 0 ) {
continue ;
}
E_deshash ( " " , zero_string_des_hash . hash ) ;
if ( ! lm_history_pwd | | memcmp ( lm_history_pwd - > hash , zero_string_des_hash . hash , 16 ) = = 0 ) {
lm_history_pwd = NULL ;
}
auth_status = authsam_password_ok ( auth_context , tmp_ctx ,
acct_flags ,
lm_history_pwd ,
nt_history_pwd ,
user_info ,
user_sess_key ,
lm_sess_key ) ;
if ( ! NT_STATUS_IS_OK ( auth_status ) ) {
/*
* If this was not a correct password , try the next
* one from the history
*/
* user_sess_key = data_blob_null ;
* lm_sess_key = data_blob_null ;
continue ;
}
if ( i ! = 1 ) {
/*
* The authentication was OK , but not against
* the previous password , which is stored at index 1.
*
* We just return the original wrong password .
* This skips the update of the bad pwd count ,
* because this is almost certainly user error
* ( or automatic login on a computer using a cached
* password from before the password change ) ,
* not an attack .
*/
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_WRONG_PASSWORD ;
}
2022-03-04 19:09:41 +01:00
if ( user_info - > flags & USER_INFO_INTERACTIVE_LOGON ) {
2013-11-11 10:38:03 +13:00
/*
* The authentication was OK against the previous password ,
2022-03-04 19:09:41 +01:00
* but it ' s not a NTLM network authentication ,
* LDAP simple bind or something similar .
2013-11-11 10:38:03 +13:00
*
* We just return the original wrong password .
* This skips the update of the bad pwd count ,
* because this is almost certainly user error
* ( or automatic login on a computer using a cached
* password from before the password change ) ,
* not an attack .
*/
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_WRONG_PASSWORD ;
}
/*
* If the password was OK , it ' s a NTLM network authentication
* and it was the previous password .
*
* Now we see if it is within the grace period ,
* so that we don ' t break cached sessions on other computers
* before the user can lock and unlock their other screens
* ( resetting their cached password ) .
*
* See http : //support.microsoft.com/kb/906305
* OldPasswordAllowedPeriod ( " old password allowed period " )
* is specified in minutes . The default is 60.
*/
allowed_period_mins = lpcfg_old_password_allowed_period ( auth_context - > lp_ctx ) ;
/*
* NTTIME uses 100 ns units
*/
2021-01-13 12:04:18 +13:00
allowed_period = ( NTTIME ) allowed_period_mins *
60 * 1000 * 1000 * 10 ;
2013-11-11 10:38:03 +13:00
pwdLastSet = samdb_result_nttime ( msg , " pwdLastSet " , 0 ) ;
2015-10-22 09:45:26 +13:00
tv_now = timeval_current ( ) ;
now = timeval_to_nttime ( & tv_now ) ;
2013-11-11 10:38:03 +13:00
if ( now < pwdLastSet ) {
/*
* time jump ?
*
* We just return the original wrong password .
* This skips the update of the bad pwd count ,
* because this is almost certainly user error
* ( or automatic login on a computer using a cached
* password from before the password change ) ,
* not an attack .
*/
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_WRONG_PASSWORD ;
}
if ( ( now - pwdLastSet ) > = allowed_period ) {
/*
* The allowed period is over .
*
* We just return the original wrong password .
* This skips the update of the bad pwd count ,
* because this is almost certainly user error
* ( or automatic login on a computer using a cached
* password from before the password change ) ,
* not an attack .
*/
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_WRONG_PASSWORD ;
}
/*
* We finally allow the authentication with the
* previous password within the allowed period .
*/
if ( user_sess_key - > data ) {
talloc_steal ( mem_ctx , user_sess_key - > data ) ;
}
if ( lm_sess_key - > data ) {
talloc_steal ( mem_ctx , lm_sess_key - > data ) ;
}
TALLOC_FREE ( tmp_ctx ) ;
return auth_status ;
}
/*
* If we are not in the allowed period or match an old password ,
* we didn ' t return early . Now update the badPwdCount et al .
*/
nt_status = authsam_update_bad_pwd_count ( auth_context - > sam_ctx ,
msg , domain_dn ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
/*
* We need to return the original
* NT_STATUS_WRONG_PASSWORD error , so there isn ' t
* anything more we can do than write something into
* the log
*/
DEBUG ( 0 , ( " Failed to note bad password for user [%s]: %s \n " ,
user_info - > mapped . account_name ,
nt_errstr ( nt_status ) ) ) ;
}
2017-04-03 15:22:08 +12:00
if ( samdb_rodc ( auth_context - > sam_ctx , & am_rodc ) = = LDB_SUCCESS & & am_rodc ) {
* authoritative = false ;
}
2013-11-11 10:38:03 +13:00
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_WRONG_PASSWORD ;
}
static NTSTATUS authsam_authenticate ( struct auth4_context * auth_context ,
TALLOC_CTX * mem_ctx , struct ldb_context * sam_ctx ,
struct ldb_dn * domain_dn ,
struct ldb_message * msg ,
const struct auth_usersupplied_info * user_info ,
2017-04-03 15:21:29 +12:00
DATA_BLOB * user_sess_key , DATA_BLOB * lm_sess_key ,
bool * authoritative )
2013-11-11 10:38:03 +13:00
{
NTSTATUS nt_status ;
auth: keep track of lastLogon and lastLogonTimestamp
lastLogon is supposed to be updated for every interactive or kerberos
login, and (according to testing against Windows2012r2) when the bad
password count is non-zero but the lockout time is zero. It is not
replicated.
lastLogonTimestamp is updated if the old value is more than 14 -
random.choice([0, 1, 2, 3, 4, 5]) days old, and it is replicated. The
14 in this calculation is the default, stored as
"msDS-LogonTimeSyncInterval", which we offer no interface for
changing.
The authsam_zero_bad_pwd_count() function is a convenient place to
update these values, as it is called upon a successful logon however
that logon is performed. That makes the function's name inaccurate, so
we rename it authsam_logon_success_accounting(). It also needs to be
told whet5her the login is interactive.
The password_lockout tests are extended to test lastLogon and
lasLogonTimestamp.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Ralph Boehme <slow@samba.org>
2015-10-23 16:57:56 +13:00
bool interactive = ( user_info - > password_state = = AUTH_PASSWORD_HASH ) ;
2016-06-02 14:14:06 +02:00
uint32_t acct_flags = samdb_result_acct_flags ( msg , NULL ) ;
2017-04-11 15:51:50 +12:00
struct netr_SendToSamBase * send_to_sam = NULL ;
2013-11-11 10:38:03 +13:00
TALLOC_CTX * tmp_ctx = talloc_new ( mem_ctx ) ;
if ( ! tmp_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
/* You can only do an interactive login to normal accounts */
if ( user_info - > flags & USER_INFO_INTERACTIVE_LOGON ) {
if ( ! ( acct_flags & ACB_NORMAL ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_NO_SUCH_USER ;
2013-11-06 10:39:42 +13:00
}
2016-06-02 14:14:06 +02:00
if ( acct_flags & ACB_SMARTCARD_REQUIRED ) {
if ( acct_flags & ACB_DISABLED ) {
DEBUG ( 2 , ( " authsam_authenticate: Account for user '%s' "
" was disabled. \n " ,
user_info - > mapped . account_name ) ) ;
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_ACCOUNT_DISABLED ;
}
DEBUG ( 2 , ( " authsam_authenticate: Account for user '%s' "
" requires interactive smartcard logon. \n " ,
user_info - > mapped . account_name ) ) ;
TALLOC_FREE ( tmp_ctx ) ;
return NT_STATUS_SMARTCARD_LOGON_REQUIRED ;
}
2013-11-06 10:39:42 +13:00
}
2013-11-11 10:38:03 +13:00
nt_status = authsam_password_check_and_record ( auth_context , tmp_ctx ,
domain_dn , msg , acct_flags ,
user_info ,
2017-04-03 15:21:29 +12:00
user_sess_key , lm_sess_key ,
authoritative ) ;
2013-11-11 10:32:58 +13:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return nt_status ;
}
2003-08-13 01:53:07 +00:00
2013-11-11 10:32:58 +13:00
nt_status = authsam_account_ok ( tmp_ctx , auth_context - > sam_ctx ,
2005-10-28 08:54:37 +00:00
user_info - > logon_parameters ,
2009-05-26 12:31:39 +10:00
domain_dn ,
2009-06-04 14:07:35 +10:00
msg ,
2005-11-06 14:16:34 +00:00
user_info - > workstation_name ,
2008-12-04 15:09:21 +01:00
user_info - > mapped . account_name ,
2009-06-18 11:08:46 +10:00
false , false ) ;
2013-11-11 10:32:58 +13:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return nt_status ;
}
auth: keep track of lastLogon and lastLogonTimestamp
lastLogon is supposed to be updated for every interactive or kerberos
login, and (according to testing against Windows2012r2) when the bad
password count is non-zero but the lockout time is zero. It is not
replicated.
lastLogonTimestamp is updated if the old value is more than 14 -
random.choice([0, 1, 2, 3, 4, 5]) days old, and it is replicated. The
14 in this calculation is the default, stored as
"msDS-LogonTimeSyncInterval", which we offer no interface for
changing.
The authsam_zero_bad_pwd_count() function is a convenient place to
update these values, as it is called upon a successful logon however
that logon is performed. That makes the function's name inaccurate, so
we rename it authsam_logon_success_accounting(). It also needs to be
told whet5her the login is interactive.
The password_lockout tests are extended to test lastLogon and
lasLogonTimestamp.
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Ralph Boehme <slow@samba.org>
2015-10-23 16:57:56 +13:00
nt_status = authsam_logon_success_accounting ( auth_context - > sam_ctx ,
msg , domain_dn ,
2017-04-11 15:51:50 +12:00
interactive ,
& send_to_sam ) ;
if ( send_to_sam ! = NULL ) {
auth_sam_trigger_zero_password ( tmp_ctx ,
auth_context - > msg_ctx ,
auth_context - > event_ctx ,
send_to_sam ) ;
}
2013-11-11 11:35:12 +13:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
TALLOC_FREE ( tmp_ctx ) ;
return nt_status ;
}
2013-11-11 10:32:58 +13:00
if ( user_sess_key & & user_sess_key - > data ) {
talloc_steal ( mem_ctx , user_sess_key - > data ) ;
}
if ( lm_sess_key & & lm_sess_key - > data ) {
talloc_steal ( mem_ctx , lm_sess_key - > data ) ;
}
TALLOC_FREE ( tmp_ctx ) ;
2004-10-25 04:25:29 +00:00
return nt_status ;
}
2005-11-05 21:26:28 +00:00
2004-10-25 04:25:29 +00:00
2005-01-09 12:55:25 +00:00
static NTSTATUS authsam_check_password_internals ( struct auth_method_context * ctx ,
TALLOC_CTX * mem_ctx ,
const struct auth_usersupplied_info * user_info ,
2017-04-03 15:21:29 +12:00
struct auth_user_info_dc * * user_info_dc ,
bool * authoritative )
2004-10-25 04:25:29 +00:00
{
2005-01-09 12:55:25 +00:00
NTSTATUS nt_status ;
2005-07-22 04:10:07 +00:00
const char * account_name = user_info - > mapped . account_name ;
2009-06-04 14:07:35 +10:00
struct ldb_message * msg ;
2009-05-26 12:31:39 +10:00
struct ldb_dn * domain_dn ;
2004-10-25 04:25:29 +00:00
DATA_BLOB user_sess_key , lm_sess_key ;
2006-01-31 03:15:16 +00:00
TALLOC_CTX * tmp_ctx ;
2017-03-17 14:57:33 +01:00
const char * p = NULL ;
2004-10-25 04:25:29 +00:00
2010-04-13 12:00:06 +10:00
if ( ctx - > auth_ctx - > sam_ctx = = NULL ) {
DEBUG ( 0 , ( " No SAM available, cannot log in users \n " ) ) ;
return NT_STATUS_INVALID_SYSTEM_SERVICE ;
}
2005-01-09 12:55:25 +00:00
if ( ! account_name | | ! * account_name ) {
/* 'not for me' */
return NT_STATUS_NOT_IMPLEMENTED ;
2004-12-23 03:00:55 +00:00
}
2006-01-31 03:15:16 +00:00
tmp_ctx = talloc_new ( mem_ctx ) ;
if ( ! tmp_ctx ) {
return NT_STATUS_NO_MEMORY ;
}
2010-04-13 12:00:06 +10:00
domain_dn = ldb_get_default_basedn ( ctx - > auth_ctx - > sam_ctx ) ;
2009-05-26 12:31:39 +10:00
if ( domain_dn = = NULL ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_SUCH_DOMAIN ;
}
2020-02-10 11:52:33 +13:00
/*
* If we have not already mapped this user , then now is a good
* time to do so , before we look it up . We used to do this
* earlier , but in a multi - forest environment we want to do
* this mapping at the final domain .
*
* However , on the flip side we may have already mapped the
* user if this was an LDAP simple bind , in which case we
* really , really want to get back to exactly the same account
* we got the DN for .
*/
if ( user_info - > mapped_state = = false ) {
p = strchr_m ( account_name , ' @ ' ) ;
} else {
/*
* This is slightly nicer than double - indenting the
* block below
*/
p = NULL ;
}
2017-03-17 14:57:33 +01:00
if ( p ! = NULL ) {
const char * nt4_domain = NULL ;
const char * nt4_account = NULL ;
bool is_my_domain = false ;
nt_status = crack_name_to_nt4_name ( mem_ctx ,
2017-06-19 08:39:19 +02:00
ctx - > auth_ctx - > sam_ctx ,
2017-03-17 14:57:33 +01:00
/*
* DRSUAPI_DS_NAME_FORMAT_UPN_FOR_LOGON ?
*/
DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL ,
account_name ,
& nt4_domain , & nt4_account ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NO_SUCH_USER ;
}
is_my_domain = lpcfg_is_mydomain ( ctx - > auth_ctx - > lp_ctx , nt4_domain ) ;
if ( ! is_my_domain ) {
/*
* This is a user within our forest ,
* but in a different domain ,
* we ' re not authoritative
*/
talloc_free ( tmp_ctx ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
/*
* Let ' s use the NT4 account name for the lookup .
*/
account_name = nt4_account ;
}
2010-04-13 12:00:06 +10:00
nt_status = authsam_search_account ( tmp_ctx , ctx - > auth_ctx - > sam_ctx , account_name , domain_dn , & msg ) ;
2006-01-31 03:15:16 +00:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return nt_status ;
}
2004-10-25 04:25:29 +00:00
2010-04-13 12:00:06 +10:00
nt_status = authsam_authenticate ( ctx - > auth_ctx , tmp_ctx , ctx - > auth_ctx - > sam_ctx , domain_dn , msg , user_info ,
2017-04-03 15:21:29 +12:00
& user_sess_key , & lm_sess_key , authoritative ) ;
2006-01-31 03:15:16 +00:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return nt_status ;
}
2004-10-25 04:25:29 +00:00
2016-01-07 14:55:07 +01:00
nt_status = authsam_make_user_info_dc ( tmp_ctx , ctx - > auth_ctx - > sam_ctx ,
lpcfg_netbios_name ( ctx - > auth_ctx - > lp_ctx ) ,
2010-07-16 14:32:42 +10:00
lpcfg_sam_name ( ctx - > auth_ctx - > lp_ctx ) ,
2016-01-07 14:55:07 +01:00
lpcfg_sam_dnsname ( ctx - > auth_ctx - > lp_ctx ) ,
2009-05-26 12:31:39 +10:00
domain_dn ,
2009-06-04 14:07:35 +10:00
msg ,
2005-01-09 12:55:25 +00:00
user_sess_key , lm_sess_key ,
2011-02-08 16:53:13 +11:00
user_info_dc ) ;
2006-01-31 03:15:16 +00:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
talloc_free ( tmp_ctx ) ;
return nt_status ;
}
2004-10-25 04:25:29 +00:00
2011-02-08 16:53:13 +11:00
talloc_steal ( mem_ctx , * user_info_dc ) ;
2006-01-31 03:15:16 +00:00
talloc_free ( tmp_ctx ) ;
2005-01-24 02:19:57 +00:00
2004-10-25 04:25:29 +00:00
return NT_STATUS_OK ;
}
2021-04-15 10:04:21 +02:00
struct authsam_check_password_state {
struct auth_user_info_dc * user_info_dc ;
bool authoritative ;
} ;
static struct tevent_req * authsam_check_password_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct auth_method_context * ctx ,
const struct auth_usersupplied_info * user_info )
{
struct tevent_req * req = NULL ;
struct authsam_check_password_state * state = NULL ;
NTSTATUS status ;
req = tevent_req_create (
mem_ctx , & state , struct authsam_check_password_state ) ;
if ( req = = NULL ) {
return NULL ;
}
/*
* authsam_check_password_internals ( ) sets this to false in
* the rodc case , otherwise it leaves it untouched . Default to
* " we're authoritative " .
*/
state - > authoritative = true ;
status = authsam_check_password_internals (
ctx ,
state ,
user_info ,
& state - > user_info_dc ,
& state - > authoritative ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
}
static NTSTATUS authsam_check_password_recv (
struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct auth_user_info_dc * * interim_info ,
bool * authoritative )
{
struct authsam_check_password_state * state = tevent_req_data (
req , struct authsam_check_password_state ) ;
NTSTATUS status ;
* authoritative = state - > authoritative ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
* interim_info = talloc_move ( mem_ctx , & state - > user_info_dc ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2006-07-27 11:24:18 +00:00
static NTSTATUS authsam_ignoredomain_want_check ( struct auth_method_context * ctx ,
TALLOC_CTX * mem_ctx ,
const struct auth_usersupplied_info * user_info )
{
if ( ! user_info - > mapped . account_name | | ! * user_info - > mapped . account_name ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
return NT_STATUS_OK ;
}
2003-08-13 01:53:07 +00:00
/****************************************************************************
Check SAM security ( above ) but with a few extra checks .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-07-27 11:24:18 +00:00
static NTSTATUS authsam_want_check ( struct auth_method_context * ctx ,
TALLOC_CTX * mem_ctx ,
const struct auth_usersupplied_info * user_info )
2003-08-13 01:53:07 +00:00
{
2017-03-17 16:31:02 +01:00
const char * effective_domain = user_info - > mapped . domain_name ;
bool is_local_name = false ;
bool is_my_domain = false ;
const char * p = NULL ;
struct dsdb_trust_routing_table * trt = NULL ;
const struct lsa_TrustDomainInfoInfoEx * tdo = NULL ;
NTSTATUS status ;
2003-08-13 01:53:07 +00:00
2006-07-27 11:24:18 +00:00
if ( ! user_info - > mapped . account_name | | ! * user_info - > mapped . account_name ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
2018-01-09 08:54:11 +01:00
if ( effective_domain = = NULL ) {
effective_domain = " " ;
}
2010-07-16 14:32:42 +10:00
is_local_name = lpcfg_is_myname ( ctx - > auth_ctx - > lp_ctx ,
2017-03-17 16:31:02 +01:00
effective_domain ) ;
2004-10-25 04:25:29 +00:00
/* check whether or not we service this domain/workgroup name */
2010-07-16 14:32:42 +10:00
switch ( lpcfg_server_role ( ctx - > auth_ctx - > lp_ctx ) ) {
2017-03-17 16:31:02 +01:00
case ROLE_STANDALONE :
return NT_STATUS_OK ;
2006-07-27 11:24:18 +00:00
2017-03-17 16:31:02 +01:00
case ROLE_DOMAIN_MEMBER :
if ( is_local_name ) {
2006-07-27 11:24:18 +00:00
return NT_STATUS_OK ;
2017-03-17 16:31:02 +01:00
}
2006-07-27 11:24:18 +00:00
2017-03-17 16:31:02 +01:00
DBG_DEBUG ( " %s is not one of my local names (DOMAIN_MEMBER) \n " ,
effective_domain ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
case ROLE_ACTIVE_DIRECTORY_DC :
/* handled later */
break ;
default :
DBG_ERR ( " lpcfg_server_role() has an undefined value \n " ) ;
return NT_STATUS_INVALID_SERVER_STATE ;
}
/*
* Now we handle the AD DC case . . .
*/
is_my_domain = lpcfg_is_my_domain_or_realm ( ctx - > auth_ctx - > lp_ctx ,
effective_domain ) ;
if ( is_my_domain ) {
return NT_STATUS_OK ;
}
if ( user_info - > mapped_state ) {
/*
* The caller already did a cracknames call .
*/
DBG_DEBUG ( " %s is not one domain name (DC) \n " ,
effective_domain ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
2018-01-09 08:54:11 +01:00
if ( ! strequal ( effective_domain , " " ) ) {
2017-03-17 16:31:02 +01:00
DBG_DEBUG ( " %s is not one domain name (DC) \n " ,
effective_domain ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
p = strchr_m ( user_info - > mapped . account_name , ' @ ' ) ;
if ( p = = NULL ) {
2018-01-09 08:54:11 +01:00
/*
* An empty to domain name should be handled
* as the local domain name .
*/
return NT_STATUS_OK ;
2017-03-17 16:31:02 +01:00
}
effective_domain = p + 1 ;
is_my_domain = lpcfg_is_my_domain_or_realm ( ctx - > auth_ctx - > lp_ctx ,
effective_domain ) ;
if ( is_my_domain ) {
return NT_STATUS_OK ;
2006-07-27 11:24:18 +00:00
}
2017-03-17 16:31:02 +01:00
if ( strequal ( effective_domain , " " ) ) {
DBG_DEBUG ( " authsam_check_password: upn without realm (DC) \n " ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
/*
* as last option we check the routing table if the
* domain is within our forest .
*/
status = dsdb_trust_routing_table_load ( ctx - > auth_ctx - > sam_ctx ,
mem_ctx , & trt ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " authsam_check_password: dsdb_trust_routing_table_load() %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
tdo = dsdb_trust_routing_by_name ( trt , effective_domain ) ;
if ( tdo = = NULL ) {
DBG_DEBUG ( " %s is not a known TLN (DC) \n " ,
effective_domain ) ;
TALLOC_FREE ( trt ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
if ( ! ( tdo - > trust_attributes & LSA_TRUST_ATTRIBUTE_WITHIN_FOREST ) ) {
DBG_DEBUG ( " %s is not a TLN in our forest (DC) \n " ,
effective_domain ) ;
TALLOC_FREE ( trt ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
/*
* This principal is within our forest .
* we ' ll later do a crack_name_to_nt4_name ( )
* to check if it ' s in our domain .
*/
TALLOC_FREE ( trt ) ;
return NT_STATUS_OK ;
2006-07-27 11:24:18 +00:00
}
2005-01-09 12:55:25 +00:00
static const struct auth_operations sam_ignoredomain_ops = {
2009-02-13 14:02:49 +11:00
. name = " sam_ignoredomain " ,
. want_check = authsam_ignoredomain_want_check ,
2021-04-15 10:04:21 +02:00
. check_password_send = authsam_check_password_send ,
. check_password_recv = authsam_check_password_recv ,
2005-01-09 12:55:25 +00:00
} ;
2003-08-13 01:53:07 +00:00
2005-01-09 12:55:25 +00:00
static const struct auth_operations sam_ops = {
2009-02-13 14:02:49 +11:00
. name = " sam " ,
. want_check = authsam_want_check ,
2021-04-15 10:04:21 +02:00
. check_password_send = authsam_check_password_send ,
. check_password_recv = authsam_check_password_recv ,
2005-01-09 12:55:25 +00:00
} ;
2004-02-03 11:10:56 +00:00
2017-04-20 12:24:43 -07:00
_PUBLIC_ NTSTATUS auth4_sam_init ( TALLOC_CTX * ) ;
_PUBLIC_ NTSTATUS auth4_sam_init ( TALLOC_CTX * ctx )
2004-02-03 11:10:56 +00:00
{
NTSTATUS ret ;
2017-05-09 12:39:14 -07:00
ret = auth_register ( ctx , & sam_ops ) ;
2004-02-03 11:10:56 +00:00
if ( ! NT_STATUS_IS_OK ( ret ) ) {
2005-01-09 12:55:25 +00:00
DEBUG ( 0 , ( " Failed to register 'sam' auth backend! \n " ) ) ;
2004-02-03 11:10:56 +00:00
return ret ;
}
2017-05-09 12:39:14 -07:00
ret = auth_register ( ctx , & sam_ignoredomain_ops ) ;
2004-02-03 11:10:56 +00:00
if ( ! NT_STATUS_IS_OK ( ret ) ) {
2005-01-09 12:55:25 +00:00
DEBUG ( 0 , ( " Failed to register 'sam_ignoredomain' auth backend! \n " ) ) ;
2004-02-03 11:10:56 +00:00
return ret ;
}
return ret ;
}