2003-07-01 21:51:52 +04:00
/*
2002-01-30 09:08:46 +03:00
* Unix SMB / CIFS implementation .
2003-03-14 20:05:13 +03:00
* Routines to operate on various trust relationships
* Copyright ( C ) Andrew Bartlett 2001
* Copyright ( C ) Rafal Szczesniak 2003
2001-12-05 14:00:26 +03: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-09 23:25:36 +04:00
* the Free Software Foundation ; either version 3 of the License , or
2001-12-05 14:00:26 +03: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 09:23:25 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2001-12-05 14:00:26 +03:00
*/
# include "includes.h"
2009-03-16 13:27:58 +03:00
# include "../libcli/auth/libcli_auth.h"
2013-09-15 15:19:52 +04:00
# include "../libcli/auth/netlogon_creds_cli.h"
2010-05-18 20:26:03 +04:00
# include "rpc_client/cli_netlogon.h"
2011-02-28 12:19:44 +03:00
# include "rpc_client/cli_pipe.h"
2010-06-30 14:10:55 +04:00
# include "../librpc/gen_ndr/ndr_netlogon.h"
2010-08-05 04:25:37 +04:00
# include "secrets.h"
2011-03-18 20:58:37 +03:00
# include "passdb.h"
2011-05-06 13:47:43 +04:00
# include "libsmb/libsmb.h"
2013-09-15 15:19:52 +04:00
# include "source3/include/messages.h"
# include "source3/include/g_lock.h"
2001-12-05 14:00:26 +03:00
/*********************************************************
Change the domain password on the PDC .
2013-09-02 22:19:28 +04:00
Do most of the legwork ourselfs . Caller must have
already setup the connection to the NETLOGON pipe
2001-12-05 14:00:26 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-09-15 15:19:52 +04:00
struct trust_pw_change_state {
struct g_lock_ctx * g_ctx ;
char * g_lock_key ;
} ;
static int trust_pw_change_state_destructor ( struct trust_pw_change_state * state )
{
g_lock_unlock ( state - > g_ctx , state - > g_lock_key ) ;
return 0 ;
}
2016-08-23 13:12:35 +03:00
char * trust_pw_new_value ( TALLOC_CTX * mem_ctx ,
enum netr_SchannelType sec_channel_type ,
int security )
{
/*
* use secure defaults .
*/
size_t min = 128 ;
size_t max = 255 ;
switch ( sec_channel_type ) {
case SEC_CHAN_WKSTA :
case SEC_CHAN_BDC :
if ( security = = SEC_DOMAIN ) {
/*
* The maximum length of a trust account password .
* Used when we randomly create it , 15 char passwords
* exceed NT4 ' s max password length .
*/
min = 14 ;
max = 14 ;
}
break ;
case SEC_CHAN_DNS_DOMAIN :
/*
* new_len * 2 = 498 bytes is the largest possible length
* NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes
* and a confounder with at least 2 bytes is required .
*
* Windows uses new_len = 120 = > 240 bytes ( utf16 )
*/
min = 120 ;
max = 120 ;
break ;
/* fall through */
case SEC_CHAN_DOMAIN :
/*
* The maximum length of a trust account password .
* Used when we randomly create it , 15 char passwords
* exceed NT4 ' s max password length .
*/
min = 14 ;
max = 14 ;
break ;
default :
break ;
}
/*
* Create a random machine account password
* We create a random buffer and convert that to utf8 .
* This is similar to what windows is doing .
*/
return generate_random_machine_password ( mem_ctx , min , max ) ;
}
2013-09-15 15:19:52 +04:00
NTSTATUS trust_pw_change ( struct netlogon_creds_cli_context * context ,
struct messaging_context * msg_ctx ,
struct dcerpc_binding_handle * b ,
const char * domain ,
2017-05-22 16:36:29 +03:00
const char * dcname ,
2013-09-15 15:19:52 +04:00
bool force )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2017-01-18 21:57:30 +03:00
const char * context_name = NULL ;
2013-09-15 15:19:52 +04:00
struct trust_pw_change_state * state ;
2015-01-30 12:21:59 +03:00
struct cli_credentials * creds = NULL ;
const struct samr_Password * current_nt_hash = NULL ;
2013-09-15 15:19:52 +04:00
const struct samr_Password * previous_nt_hash = NULL ;
enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL ;
time_t pass_last_set_time ;
2015-01-30 12:21:59 +03:00
uint32_t old_version = 0 ;
2015-01-30 12:21:59 +03:00
struct pdb_trusted_domain * td = NULL ;
2013-09-15 15:19:52 +04:00
struct timeval g_timeout = { 0 , } ;
int timeout = 0 ;
struct timeval tv = { 0 , } ;
2015-01-30 12:21:59 +03:00
char * new_trust_passwd = NULL ;
2015-01-30 12:21:59 +03:00
uint32_t new_version = 0 ;
uint32_t * new_trust_version = NULL ;
2013-09-15 15:19:52 +04:00
NTSTATUS status ;
2015-01-30 12:21:59 +03:00
bool ok ;
2013-09-15 15:19:52 +04:00
state = talloc_zero ( frame , struct trust_pw_change_state ) ;
if ( state = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
state - > g_ctx = g_lock_ctx_init ( state , msg_ctx ) ;
if ( state - > g_ctx = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
state - > g_lock_key = talloc_asprintf ( state ,
" trust_password_change_%s " ,
domain ) ;
if ( state - > g_lock_key = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
g_timeout = timeval_current_ofs ( 10 , 0 ) ;
status = g_lock_lock ( state - > g_ctx ,
state - > g_lock_key ,
G_LOCK_WRITE , g_timeout ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " could not get g_lock on [%s]! \n " ,
state - > g_lock_key ) ) ;
TALLOC_FREE ( frame ) ;
return status ;
}
talloc_set_destructor ( state , trust_pw_change_state_destructor ) ;
2015-01-30 12:21:59 +03:00
status = pdb_get_trust_credentials ( domain , NULL , frame , & creds ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " could not fetch domain creds for domain %s - %s! \n " ,
domain , nt_errstr ( status ) ) ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE ;
}
current_nt_hash = cli_credentials_get_nt_hash ( creds , frame ) ;
if ( current_nt_hash = = NULL ) {
DEBUG ( 0 , ( " cli_credentials_get_nt_hash failed for domain %s! \n " ,
domain ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE ;
}
2015-01-30 12:21:59 +03:00
old_version = cli_credentials_get_kvno ( creds ) ;
2015-01-30 12:21:59 +03:00
pass_last_set_time = cli_credentials_get_password_last_changed_time ( creds ) ;
sec_channel_type = cli_credentials_get_secure_channel_type ( creds ) ;
2015-01-30 12:21:59 +03:00
new_version = old_version + 1 ;
2013-09-15 15:19:52 +04:00
switch ( sec_channel_type ) {
case SEC_CHAN_WKSTA :
2014-10-23 03:28:48 +04:00
case SEC_CHAN_BDC :
2013-09-15 15:19:52 +04:00
break ;
2015-01-30 12:21:59 +03:00
case SEC_CHAN_DNS_DOMAIN :
2013-09-15 15:19:52 +04:00
case SEC_CHAN_DOMAIN :
2015-01-30 12:21:59 +03:00
status = pdb_get_trusted_domain ( frame , domain , & td ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " pdb_get_trusted_domain() failed for domain %s - %s! \n " ,
domain , nt_errstr ( status ) ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
2015-01-30 12:21:59 +03:00
return status ;
2013-09-15 15:19:52 +04:00
}
2015-01-30 12:21:59 +03:00
new_trust_version = & new_version ;
2013-09-15 15:19:52 +04:00
break ;
default :
TALLOC_FREE ( frame ) ;
return NT_STATUS_NOT_SUPPORTED ;
}
timeout = lp_machine_password_timeout ( ) ;
if ( timeout = = 0 ) {
if ( ! force ) {
DEBUG ( 10 , ( " machine password never expires \n " ) ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}
}
tv . tv_sec = pass_last_set_time ;
DEBUG ( 10 , ( " password last changed %s \n " ,
timeval_string ( talloc_tos ( ) , & tv , false ) ) ) ;
tv . tv_sec + = timeout ;
DEBUGADD ( 10 , ( " password valid until %s \n " ,
timeval_string ( talloc_tos ( ) , & tv , false ) ) ) ;
if ( ! force & & ! timeval_expired ( & tv ) ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}
2017-01-18 21:57:30 +03:00
context_name = netlogon_creds_cli_debug_string ( context , talloc_tos ( ) ) ;
if ( context_name = = NULL ) {
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
2015-01-30 12:21:59 +03:00
/*
* Create a random machine account password
* We create a random buffer and convert that to utf8 .
* This is similar to what windows is doing .
*/
2016-08-23 13:12:35 +03:00
new_trust_passwd = trust_pw_new_value ( frame , sec_channel_type ,
lp_security ( ) ) ;
if ( new_trust_passwd = = NULL ) {
DEBUG ( 0 , ( " trust_pw_new_value() failed \n " ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
2015-01-31 13:42:09 +03:00
/*
* We could use cli_credentials_get_old_nt_hash ( creds , frame ) to
* set previous_nt_hash .
*
* But we want to check if the dc has our current password and only do
* a change if that ' s the case . So we keep previous_nt_hash = NULL .
*
* TODO :
* If the previous password is the only password in common with the dc ,
* we better skip the password change , or use something like
* ServerTrustPasswordsGet ( ) or netr_ServerGetTrustInfo ( ) to fix our
* local secrets before doing the change .
*/
2013-09-15 15:19:52 +04:00
status = netlogon_creds_cli_auth ( context , b ,
2015-01-30 12:21:59 +03:00
* current_nt_hash ,
2013-09-15 15:19:52 +04:00
previous_nt_hash ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-01-18 21:57:30 +03:00
DEBUG ( 0 , ( " netlogon_creds_cli_auth(%s) failed for old password - %s! \n " ,
context_name , nt_errstr ( status ) ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return status ;
}
2017-01-18 21:57:30 +03:00
DEBUG ( 0 , ( " %s : %s(%s): Verified old password remotely using %s \n " ,
current_timestring ( talloc_tos ( ) , false ) ,
__func__ , domain , context_name ) ) ;
2013-09-15 15:19:52 +04:00
/*
* Return the result of trying to write the new password
* back into the trust account file .
*/
switch ( sec_channel_type ) {
case SEC_CHAN_WKSTA :
2014-10-23 03:28:48 +04:00
case SEC_CHAN_BDC :
2015-01-30 12:21:59 +03:00
ok = secrets_store_machine_password ( new_trust_passwd , domain , sec_channel_type ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " secrets_store_machine_password failed for domain %s! \n " ,
domain ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
break ;
2015-01-30 12:21:59 +03:00
case SEC_CHAN_DNS_DOMAIN :
2013-09-15 15:19:52 +04:00
case SEC_CHAN_DOMAIN :
/*
* we need to get the sid first for the
* pdb_set_trusteddom_pw call
*/
2015-01-30 12:21:59 +03:00
ok = pdb_set_trusteddom_pw ( domain , new_trust_passwd ,
& td - > security_identifier ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " pdb_set_trusteddom_pw() failed for domain %s! \n " ,
domain ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
break ;
default :
2014-10-23 03:28:48 +04:00
smb_panic ( " Unsupported secure channel type " ) ;
2013-09-15 15:19:52 +04:00
break ;
}
2017-01-18 21:57:30 +03:00
DEBUG ( 0 , ( " %s : %s(%s): Changed password locally \n " ,
2015-01-31 13:42:09 +03:00
current_timestring ( talloc_tos ( ) , false ) , __func__ , domain ) ) ;
status = netlogon_creds_cli_ServerPasswordSet ( context , b ,
2015-01-30 12:21:59 +03:00
new_trust_passwd ,
new_trust_version ) ;
2015-01-31 13:42:09 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-01-18 21:57:30 +03:00
DEBUG ( 0 , ( " %s : %s(%s) remote password change set with %s failed - %s \n " ,
current_timestring ( talloc_tos ( ) , false ) ,
__func__ , domain , context_name ,
nt_errstr ( status ) ) ) ;
2015-01-31 13:42:09 +03:00
TALLOC_FREE ( frame ) ;
return status ;
}
2017-01-18 21:57:30 +03:00
DEBUG ( 0 , ( " %s : %s(%s): Changed password remotely using %s \n " ,
current_timestring ( talloc_tos ( ) , false ) ,
__func__ , domain , context_name ) ) ;
2015-01-31 13:42:09 +03:00
2017-02-10 00:53:52 +03:00
ok = cli_credentials_set_password ( creds , new_trust_passwd , CRED_SPECIFIED ) ;
if ( ! ok ) {
DEBUG ( 0 , ( " cli_credentials_set_password failed for domain %s! \n " ,
domain ) ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
current_nt_hash = cli_credentials_get_nt_hash ( creds , frame ) ;
if ( current_nt_hash = = NULL ) {
DEBUG ( 0 , ( " cli_credentials_get_nt_hash failed for domain %s! \n " ,
domain ) ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE ;
}
/*
* Now we verify the new password .
*/
status = netlogon_creds_cli_auth ( context , b ,
* current_nt_hash ,
NULL ) ; /* previous_nt_hash */
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " netlogon_creds_cli_auth(%s) failed for new password - %s! \n " ,
context_name , nt_errstr ( status ) ) ) ;
TALLOC_FREE ( frame ) ;
return status ;
}
DEBUG ( 0 , ( " %s : %s(%s): Verified new password remotely using %s \n " ,
current_timestring ( talloc_tos ( ) , false ) ,
__func__ , domain , context_name ) ) ;
2013-09-15 15:19:52 +04:00
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}