2013-04-18 21:16:42 +04:00
/*
Unix SMB / CIFS implementation .
module to store / fetch session keys for the schannel client
Copyright ( C ) Stefan Metzmacher 2013
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 "system/filesys.h"
# include <tevent.h>
# include "lib/util/tevent_ntstatus.h"
# include "lib/dbwrap/dbwrap.h"
# include "lib/dbwrap/dbwrap_rbt.h"
# include "lib/util/util_tdb.h"
# include "libcli/security/security.h"
# include "../lib/param/param.h"
# include "../libcli/auth/schannel.h"
# include "../librpc/gen_ndr/ndr_schannel.h"
# include "../librpc/gen_ndr/ndr_netlogon_c.h"
2017-04-11 06:51:50 +03:00
# include "../librpc/gen_ndr/ndr_netlogon.h"
2013-04-18 21:16:42 +04:00
# include "../librpc/gen_ndr/server_id.h"
# include "netlogon_creds_cli.h"
# include "source3/include/messages.h"
# include "source3/include/g_lock.h"
2015-12-28 22:01:54 +03:00
# include "libds/common/roles.h"
2019-02-27 09:59:18 +03:00
# include "lib/crypto/md4.h"
2017-09-07 13:36:14 +03:00
# include "auth/credentials/credentials.h"
2020-09-03 16:58:56 +03:00
# include "lib/param/loadparm.h"
2013-04-18 21:16:42 +04:00
struct netlogon_creds_cli_locked_state ;
struct netlogon_creds_cli_context {
struct {
const char * computer ;
const char * account ;
uint32_t proposed_flags ;
uint32_t required_flags ;
enum netr_SchannelType type ;
enum dcerpc_AuthLevel auth_level ;
} client ;
struct {
const char * computer ;
const char * netbios_domain ;
2017-09-06 14:29:07 +03:00
const char * dns_domain ;
2013-04-18 21:16:42 +04:00
uint32_t cached_flags ;
bool try_validation6 ;
bool try_logon_ex ;
bool try_logon_with ;
} server ;
struct {
const char * key_name ;
TDB_DATA key_data ;
struct db_context * ctx ;
struct g_lock_ctx * g_ctx ;
struct netlogon_creds_cli_locked_state * locked_state ;
2017-09-12 02:48:27 +03:00
enum netlogon_creds_cli_lck_type lock ;
2013-04-18 21:16:42 +04:00
} db ;
} ;
struct netlogon_creds_cli_locked_state {
struct netlogon_creds_cli_context * context ;
bool is_glocked ;
struct netlogon_creds_CredentialState * creds ;
} ;
static int netlogon_creds_cli_locked_state_destructor (
struct netlogon_creds_cli_locked_state * state )
{
struct netlogon_creds_cli_context * context = state - > context ;
if ( context = = NULL ) {
return 0 ;
}
if ( context - > db . locked_state = = state ) {
context - > db . locked_state = NULL ;
}
if ( state - > is_glocked ) {
g_lock_unlock ( context - > db . g_ctx ,
2017-12-03 22:47:02 +03:00
string_term_tdb_data ( context - > db . key_name ) ) ;
2013-04-18 21:16:42 +04:00
}
return 0 ;
}
static NTSTATUS netlogon_creds_cli_context_common (
const char * client_computer ,
const char * client_account ,
enum netr_SchannelType type ,
enum dcerpc_AuthLevel auth_level ,
uint32_t proposed_flags ,
uint32_t required_flags ,
const char * server_computer ,
const char * server_netbios_domain ,
2017-09-06 14:29:07 +03:00
const char * server_dns_domain ,
2013-04-18 21:16:42 +04:00
TALLOC_CTX * mem_ctx ,
struct netlogon_creds_cli_context * * _context )
{
struct netlogon_creds_cli_context * context = NULL ;
2013-12-13 20:31:45 +04:00
char * _key_name = NULL ;
2017-08-21 12:34:45 +03:00
size_t server_netbios_name_len ;
2013-12-13 20:31:45 +04:00
char * p = NULL ;
2013-04-18 21:16:42 +04:00
* _context = NULL ;
context = talloc_zero ( mem_ctx , struct netlogon_creds_cli_context ) ;
if ( context = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
context - > client . computer = talloc_strdup ( context , client_computer ) ;
if ( context - > client . computer = = NULL ) {
2013-12-13 20:31:45 +04:00
TALLOC_FREE ( context ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_NO_MEMORY ;
}
context - > client . account = talloc_strdup ( context , client_account ) ;
if ( context - > client . account = = NULL ) {
2013-12-13 20:31:45 +04:00
TALLOC_FREE ( context ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_NO_MEMORY ;
}
context - > client . proposed_flags = proposed_flags ;
context - > client . required_flags = required_flags ;
context - > client . type = type ;
context - > client . auth_level = auth_level ;
context - > server . computer = talloc_strdup ( context , server_computer ) ;
if ( context - > server . computer = = NULL ) {
2013-12-13 20:31:45 +04:00
TALLOC_FREE ( context ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_NO_MEMORY ;
}
context - > server . netbios_domain = talloc_strdup ( context , server_netbios_domain ) ;
if ( context - > server . netbios_domain = = NULL ) {
2013-12-13 20:31:45 +04:00
TALLOC_FREE ( context ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_NO_MEMORY ;
}
2017-09-06 14:29:07 +03:00
context - > server . dns_domain = talloc_strdup ( context , server_dns_domain ) ;
if ( context - > server . dns_domain = = NULL ) {
TALLOC_FREE ( context ) ;
return NT_STATUS_NO_MEMORY ;
}
2013-12-13 20:31:45 +04:00
/*
* TODO :
* Force the callers to provide a unique
* value for server_computer and use this directly .
*
* For now we have to deal with
* " HOSTNAME " vs . " hostname.example.com " .
*/
2017-08-21 12:34:45 +03:00
p = strchr ( server_computer , ' . ' ) ;
2013-12-13 20:31:45 +04:00
if ( p ! = NULL ) {
2017-08-21 12:34:45 +03:00
server_netbios_name_len = p - server_computer ;
} else {
server_netbios_name_len = strlen ( server_computer ) ;
2013-12-13 20:31:45 +04:00
}
2017-08-21 12:34:45 +03:00
_key_name = talloc_asprintf ( context , " CLI[%s/%s]/SRV[%.*s/%s] " ,
2013-12-13 20:31:45 +04:00
client_computer ,
client_account ,
2017-08-21 12:34:45 +03:00
( int ) server_netbios_name_len ,
server_computer ,
2013-12-13 20:31:45 +04:00
server_netbios_domain ) ;
if ( _key_name = = NULL ) {
TALLOC_FREE ( context ) ;
return NT_STATUS_NO_MEMORY ;
}
context - > db . key_name = talloc_strdup_upper ( context , _key_name ) ;
2017-08-21 12:34:45 +03:00
TALLOC_FREE ( _key_name ) ;
2013-04-18 21:16:42 +04:00
if ( context - > db . key_name = = NULL ) {
2013-12-13 20:31:45 +04:00
TALLOC_FREE ( context ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_NO_MEMORY ;
}
context - > db . key_data = string_term_tdb_data ( context - > db . key_name ) ;
* _context = context ;
return NT_STATUS_OK ;
}
static struct db_context * netlogon_creds_cli_global_db ;
2022-11-30 16:46:59 +03:00
NTSTATUS netlogon_creds_cli_set_global_db ( struct loadparm_context * lp_ctx ,
struct db_context * * db )
2014-01-17 17:00:27 +04:00
{
2022-11-30 16:47:33 +03:00
netlogon_creds_cli_warn_options ( lp_ctx ) ;
2014-01-17 17:00:27 +04:00
if ( netlogon_creds_cli_global_db ! = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2017-07-25 02:14:00 +03:00
netlogon_creds_cli_global_db = talloc_move ( NULL , db ) ;
2014-01-17 17:00:27 +04:00
return NT_STATUS_OK ;
}
2013-04-18 21:16:42 +04:00
NTSTATUS netlogon_creds_cli_open_global_db ( struct loadparm_context * lp_ctx )
{
char * fname ;
struct db_context * global_db ;
2018-04-17 17:39:46 +03:00
int hash_size , tdb_flags ;
2013-04-18 21:16:42 +04:00
2022-11-30 16:47:33 +03:00
netlogon_creds_cli_warn_options ( lp_ctx ) ;
2013-04-18 21:16:42 +04:00
if ( netlogon_creds_cli_global_db ! = NULL ) {
return NT_STATUS_OK ;
}
2017-07-24 22:56:15 +03:00
fname = lpcfg_private_db_path ( NULL , lp_ctx , " netlogon_creds_cli " ) ;
2013-04-18 21:16:42 +04:00
if ( fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2018-04-17 17:38:27 +03:00
hash_size = lpcfg_tdb_hash_size ( lp_ctx , fname ) ;
2018-04-17 17:39:46 +03:00
tdb_flags = lpcfg_tdb_flags (
lp_ctx ,
TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ) ;
2018-04-17 17:38:27 +03:00
global_db = dbwrap_local_open (
NULL ,
fname ,
hash_size ,
2018-04-17 17:39:46 +03:00
tdb_flags ,
2018-04-17 17:38:27 +03:00
O_RDWR | O_CREAT ,
0600 ,
DBWRAP_LOCK_ORDER_2 ,
DBWRAP_FLAG_NONE ) ;
2013-04-18 21:16:42 +04:00
if ( global_db = = NULL ) {
DEBUG ( 0 , ( " netlogon_creds_cli_open_global_db: Failed to open %s - %s \n " ,
fname , strerror ( errno ) ) ) ;
talloc_free ( fname ) ;
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( fname ) ;
netlogon_creds_cli_global_db = global_db ;
return NT_STATUS_OK ;
}
2017-07-25 00:49:47 +03:00
void netlogon_creds_cli_close_global_db ( void )
{
TALLOC_FREE ( netlogon_creds_cli_global_db ) ;
}
2022-11-30 16:47:33 +03:00
void netlogon_creds_cli_warn_options ( struct loadparm_context * lp_ctx )
{
bool global_reject_md5_servers = lpcfg_reject_md5_servers ( lp_ctx ) ;
bool global_require_strong_key = lpcfg_require_strong_key ( lp_ctx ) ;
int global_client_schannel = lpcfg_client_schannel ( lp_ctx ) ;
bool global_seal_secure_channel = lpcfg_winbind_sealed_pipes ( lp_ctx ) ;
2022-12-05 23:45:08 +03:00
int global_kerberos_enctypes = lpcfg_kerberos_encryption_types ( lp_ctx ) ;
2022-11-30 16:47:33 +03:00
static bool warned_global_reject_md5_servers = false ;
static bool warned_global_require_strong_key = false ;
static bool warned_global_client_schannel = false ;
static bool warned_global_seal_secure_channel = false ;
2022-12-05 23:45:08 +03:00
static bool warned_global_kerberos_encryption_types = false ;
2022-11-30 16:47:33 +03:00
static int warned_global_pid = 0 ;
int current_pid = tevent_cached_getpid ( ) ;
if ( warned_global_pid ! = current_pid ) {
warned_global_reject_md5_servers = false ;
warned_global_require_strong_key = false ;
warned_global_client_schannel = false ;
warned_global_seal_secure_channel = false ;
2022-12-05 23:45:08 +03:00
warned_global_kerberos_encryption_types = false ;
2022-11-30 16:47:33 +03:00
warned_global_pid = current_pid ;
}
if ( ! global_reject_md5_servers & & ! warned_global_reject_md5_servers ) {
/*
* We want admins to notice their misconfiguration !
*/
DBG_ERR ( " CVE-2022-38023 (and others): "
" Please configure 'reject md5 servers = yes' (the default), "
" See https://bugzilla.samba.org/show_bug.cgi?id=15240 \n " ) ;
warned_global_reject_md5_servers = true ;
}
if ( ! global_require_strong_key & & ! warned_global_require_strong_key ) {
/*
* We want admins to notice their misconfiguration !
*/
DBG_ERR ( " CVE-2022-38023 (and others): "
" Please configure 'require strong key = yes' (the default), "
" See https://bugzilla.samba.org/show_bug.cgi?id=15240 \n " ) ;
warned_global_require_strong_key = true ;
}
if ( global_client_schannel ! = true & & ! warned_global_client_schannel ) {
/*
* We want admins to notice their misconfiguration !
*/
DBG_ERR ( " CVE-2022-38023 (and others): "
" Please configure 'client schannel = yes' (the default), "
" See https://bugzilla.samba.org/show_bug.cgi?id=15240 \n " ) ;
warned_global_client_schannel = true ;
}
if ( ! global_seal_secure_channel & & ! warned_global_seal_secure_channel ) {
/*
* We want admins to notice their misconfiguration !
*/
DBG_ERR ( " CVE-2022-38023 (and others): "
" Please configure 'winbind sealed pipes = yes' (the default), "
" See https://bugzilla.samba.org/show_bug.cgi?id=15240 \n " ) ;
warned_global_seal_secure_channel = true ;
}
2022-12-05 23:45:08 +03:00
if ( global_kerberos_enctypes = = KERBEROS_ETYPES_LEGACY & &
! warned_global_kerberos_encryption_types )
{
/*
* We want admins to notice their misconfiguration !
*/
DBG_ERR ( " CVE-2022-37966: "
" Please void 'kerberos encryption types = legacy', "
" See https://bugzilla.samba.org/show_bug.cgi?id=15237 \n " ) ;
warned_global_kerberos_encryption_types = true ;
}
2022-11-30 16:47:33 +03:00
}
2013-04-18 21:16:42 +04:00
NTSTATUS netlogon_creds_cli_context_global ( struct loadparm_context * lp_ctx ,
struct messaging_context * msg_ctx ,
const char * client_account ,
enum netr_SchannelType type ,
const char * server_computer ,
const char * server_netbios_domain ,
2017-09-06 14:32:34 +03:00
const char * server_dns_domain ,
2013-04-18 21:16:42 +04:00
TALLOC_CTX * mem_ctx ,
struct netlogon_creds_cli_context * * _context )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
NTSTATUS status ;
struct netlogon_creds_cli_context * context = NULL ;
const char * client_computer ;
uint32_t proposed_flags ;
uint32_t required_flags = 0 ;
2022-11-24 20:22:23 +03:00
bool reject_md5_servers = true ;
bool require_strong_key = true ;
2013-04-18 21:16:42 +04:00
int require_sign_or_seal = true ;
bool seal_secure_channel = true ;
enum dcerpc_AuthLevel auth_level = DCERPC_AUTH_LEVEL_NONE ;
bool neutralize_nt4_emulation = false ;
* _context = NULL ;
2017-09-05 15:56:58 +03:00
if ( msg_ctx = = NULL ) {
2017-09-13 14:10:59 +03:00
TALLOC_FREE ( frame ) ;
2017-09-05 15:56:58 +03:00
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
2014-01-17 17:08:59 +04:00
client_computer = lpcfg_netbios_name ( lp_ctx ) ;
if ( strlen ( client_computer ) > 15 ) {
2017-09-13 14:10:59 +03:00
TALLOC_FREE ( frame ) ;
2014-01-17 17:08:59 +04:00
return NT_STATUS_INVALID_PARAMETER_MIX ;
2013-04-18 21:16:42 +04:00
}
/*
* allow overwrite per domain
* reject md5 servers : < netbios_domain >
*/
2013-10-17 20:48:15 +04:00
reject_md5_servers = lpcfg_reject_md5_servers ( lp_ctx ) ;
2013-04-18 21:16:42 +04:00
reject_md5_servers = lpcfg_parm_bool ( lp_ctx , NULL ,
" reject md5 servers " ,
server_netbios_domain ,
reject_md5_servers ) ;
/*
* allow overwrite per domain
* require strong key : < netbios_domain >
*/
2013-10-17 20:48:15 +04:00
require_strong_key = lpcfg_require_strong_key ( lp_ctx ) ;
2013-04-18 21:16:42 +04:00
require_strong_key = lpcfg_parm_bool ( lp_ctx , NULL ,
" require strong key " ,
server_netbios_domain ,
require_strong_key ) ;
/*
* allow overwrite per domain
* client schannel : < netbios_domain >
*/
require_sign_or_seal = lpcfg_client_schannel ( lp_ctx ) ;
require_sign_or_seal = lpcfg_parm_int ( lp_ctx , NULL ,
" client schannel " ,
server_netbios_domain ,
require_sign_or_seal ) ;
/*
* allow overwrite per domain
* winbind sealed pipes : < netbios_domain >
*/
seal_secure_channel = lpcfg_winbind_sealed_pipes ( lp_ctx ) ;
seal_secure_channel = lpcfg_parm_bool ( lp_ctx , NULL ,
" winbind sealed pipes " ,
server_netbios_domain ,
seal_secure_channel ) ;
/*
* allow overwrite per domain
* neutralize nt4 emulation : < netbios_domain >
*/
2013-10-17 20:48:15 +04:00
neutralize_nt4_emulation = lpcfg_neutralize_nt4_emulation ( lp_ctx ) ;
2013-04-18 21:16:42 +04:00
neutralize_nt4_emulation = lpcfg_parm_bool ( lp_ctx , NULL ,
" neutralize nt4 emulation " ,
server_netbios_domain ,
neutralize_nt4_emulation ) ;
proposed_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS ;
proposed_flags | = NETLOGON_NEG_SUPPORTS_AES ;
switch ( type ) {
case SEC_CHAN_WKSTA :
if ( lpcfg_security ( lp_ctx ) = = SEC_ADS ) {
/*
* AD domains should be secure
*/
required_flags | = NETLOGON_NEG_PASSWORD_SET2 ;
require_sign_or_seal = true ;
require_strong_key = true ;
}
break ;
case SEC_CHAN_DOMAIN :
break ;
case SEC_CHAN_DNS_DOMAIN :
/*
* AD domains should be secure
*/
required_flags | = NETLOGON_NEG_PASSWORD_SET2 ;
require_sign_or_seal = true ;
require_strong_key = true ;
neutralize_nt4_emulation = true ;
break ;
case SEC_CHAN_BDC :
required_flags | = NETLOGON_NEG_PASSWORD_SET2 ;
require_sign_or_seal = true ;
require_strong_key = true ;
break ;
case SEC_CHAN_RODC :
required_flags | = NETLOGON_NEG_RODC_PASSTHROUGH ;
required_flags | = NETLOGON_NEG_PASSWORD_SET2 ;
require_sign_or_seal = true ;
require_strong_key = true ;
neutralize_nt4_emulation = true ;
break ;
default :
TALLOC_FREE ( frame ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
if ( neutralize_nt4_emulation ) {
proposed_flags | = NETLOGON_NEG_NEUTRALIZE_NT4_EMULATION ;
}
2017-08-21 12:54:29 +03:00
if ( require_sign_or_seal ) {
2013-04-18 21:16:42 +04:00
required_flags | = NETLOGON_NEG_ARCFOUR ;
required_flags | = NETLOGON_NEG_AUTHENTICATED_RPC ;
2017-08-21 12:54:29 +03:00
} else {
proposed_flags & = ~ NETLOGON_NEG_AUTHENTICATED_RPC ;
2013-04-18 21:16:42 +04:00
}
if ( reject_md5_servers ) {
required_flags | = NETLOGON_NEG_ARCFOUR ;
required_flags | = NETLOGON_NEG_PASSWORD_SET2 ;
required_flags | = NETLOGON_NEG_SUPPORTS_AES ;
required_flags | = NETLOGON_NEG_AUTHENTICATED_RPC ;
}
if ( require_strong_key ) {
required_flags | = NETLOGON_NEG_ARCFOUR ;
required_flags | = NETLOGON_NEG_STRONG_KEYS ;
required_flags | = NETLOGON_NEG_AUTHENTICATED_RPC ;
}
2020-09-03 16:58:56 +03:00
/*
* If weak crypto is disabled , do not announce that we support RC4 and
* require AES .
*/
if ( lpcfg_weak_crypto ( lp_ctx ) = = SAMBA_WEAK_CRYPTO_DISALLOWED ) {
required_flags | = NETLOGON_NEG_SUPPORTS_AES ;
}
2013-04-18 21:16:42 +04:00
proposed_flags | = required_flags ;
2024-10-10 13:31:18 +03:00
if ( required_flags & NETLOGON_NEG_SUPPORTS_AES ) {
required_flags & = ~ NETLOGON_NEG_ARCFOUR ;
required_flags & = ~ NETLOGON_NEG_STRONG_KEYS ;
}
2013-04-18 21:16:42 +04:00
if ( seal_secure_channel ) {
auth_level = DCERPC_AUTH_LEVEL_PRIVACY ;
} else {
auth_level = DCERPC_AUTH_LEVEL_INTEGRITY ;
}
2024-11-07 16:44:21 +03:00
if ( server_dns_domain = = NULL ) {
server_dns_domain = " " ;
}
2013-04-18 21:16:42 +04:00
status = netlogon_creds_cli_context_common ( client_computer ,
client_account ,
type ,
auth_level ,
proposed_flags ,
required_flags ,
server_computer ,
server_netbios_domain ,
2024-11-07 16:44:21 +03:00
server_dns_domain ,
2013-04-18 21:16:42 +04:00
mem_ctx ,
& context ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return status ;
}
2017-09-05 15:56:58 +03:00
context - > db . g_ctx = g_lock_ctx_init ( context , msg_ctx ) ;
if ( context - > db . g_ctx = = NULL ) {
TALLOC_FREE ( context ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
2013-04-18 21:16:42 +04:00
}
status = netlogon_creds_cli_open_global_db ( lp_ctx ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( context ) ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_NO_MEMORY ;
}
context - > db . ctx = netlogon_creds_cli_global_db ;
* _context = context ;
TALLOC_FREE ( frame ) ;
return NT_STATUS_OK ;
}
2017-09-07 13:36:14 +03:00
NTSTATUS netlogon_creds_bind_cli_credentials (
struct netlogon_creds_cli_context * context , TALLOC_CTX * mem_ctx ,
struct cli_credentials * * pcli_creds )
{
struct cli_credentials * cli_creds ;
struct netlogon_creds_CredentialState * ncreds ;
NTSTATUS status ;
cli_creds = cli_credentials_init ( mem_ctx ) ;
if ( cli_creds = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
cli_credentials_set_secure_channel_type ( cli_creds ,
context - > client . type ) ;
cli_credentials_set_username ( cli_creds , context - > client . account ,
CRED_SPECIFIED ) ;
cli_credentials_set_domain ( cli_creds , context - > server . netbios_domain ,
CRED_SPECIFIED ) ;
cli_credentials_set_realm ( cli_creds , context - > server . dns_domain ,
CRED_SPECIFIED ) ;
status = netlogon_creds_cli_get ( context , cli_creds , & ncreds ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( cli_creds ) ;
return status ;
}
2017-09-07 13:43:00 +03:00
cli_credentials_set_netlogon_creds ( cli_creds , ncreds ) ;
2017-09-07 13:36:14 +03:00
* pcli_creds = cli_creds ;
return NT_STATUS_OK ;
}
2017-02-09 23:47:52 +03:00
char * netlogon_creds_cli_debug_string (
const struct netlogon_creds_cli_context * context ,
TALLOC_CTX * mem_ctx )
{
return talloc_asprintf ( mem_ctx , " netlogon_creds_cli:%s " ,
context - > db . key_name ) ;
}
2013-04-18 21:16:42 +04:00
enum dcerpc_AuthLevel netlogon_creds_cli_auth_level (
struct netlogon_creds_cli_context * context )
{
return context - > client . auth_level ;
}
2021-11-18 15:46:26 +03:00
static bool netlogon_creds_cli_downgraded ( uint32_t negotiated_flags ,
uint32_t proposed_flags ,
uint32_t required_flags )
{
uint32_t req_flags = required_flags ;
uint32_t tmp_flags ;
req_flags = required_flags ;
if ( ( negotiated_flags & NETLOGON_NEG_SUPPORTS_AES ) & &
( proposed_flags & NETLOGON_NEG_SUPPORTS_AES ) )
{
req_flags & = ~ NETLOGON_NEG_ARCFOUR | NETLOGON_NEG_STRONG_KEYS ;
}
tmp_flags = negotiated_flags ;
tmp_flags & = req_flags ;
if ( tmp_flags ! = req_flags ) {
return true ;
}
return false ;
}
2013-04-18 21:16:42 +04:00
struct netlogon_creds_cli_fetch_state {
TALLOC_CTX * mem_ctx ;
struct netlogon_creds_CredentialState * creds ;
2021-11-18 15:46:26 +03:00
uint32_t proposed_flags ;
2013-04-18 21:16:42 +04:00
uint32_t required_flags ;
NTSTATUS status ;
} ;
static void netlogon_creds_cli_fetch_parser ( TDB_DATA key , TDB_DATA data ,
void * private_data )
{
struct netlogon_creds_cli_fetch_state * state =
( struct netlogon_creds_cli_fetch_state * ) private_data ;
enum ndr_err_code ndr_err ;
DATA_BLOB blob ;
2021-11-18 15:46:26 +03:00
bool downgraded ;
2013-04-18 21:16:42 +04:00
state - > creds = talloc_zero ( state - > mem_ctx ,
struct netlogon_creds_CredentialState ) ;
if ( state - > creds = = NULL ) {
state - > status = NT_STATUS_NO_MEMORY ;
return ;
}
blob . data = data . dptr ;
blob . length = data . dsize ;
ndr_err = ndr_pull_struct_blob ( & blob , state - > creds , state - > creds ,
( ndr_pull_flags_fn_t ) ndr_pull_netlogon_creds_CredentialState ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( state - > creds ) ;
state - > status = ndr_map_error2ntstatus ( ndr_err ) ;
return ;
}
2017-09-10 20:11:21 +03:00
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( netlogon_creds_CredentialState , state - > creds ) ;
}
2024-11-07 19:32:07 +03:00
if ( state - > proposed_flags ! = state - > creds - > client_requested_flags ) {
TALLOC_FREE ( state - > creds ) ;
state - > status = NT_STATUS_RESOURCE_REQUIREMENTS_CHANGED ;
return ;
}
2021-11-18 15:46:26 +03:00
downgraded = netlogon_creds_cli_downgraded (
state - > creds - > negotiate_flags ,
state - > proposed_flags ,
state - > required_flags ) ;
if ( downgraded ) {
2013-04-18 21:16:42 +04:00
TALLOC_FREE ( state - > creds ) ;
state - > status = NT_STATUS_DOWNGRADE_DETECTED ;
return ;
}
state - > status = NT_STATUS_OK ;
}
2017-09-13 21:40:24 +03:00
static NTSTATUS netlogon_creds_cli_get_internal (
struct netlogon_creds_cli_context * context ,
TALLOC_CTX * mem_ctx , struct netlogon_creds_CredentialState * * pcreds ) ;
2013-04-18 21:16:42 +04:00
NTSTATUS netlogon_creds_cli_get ( struct netlogon_creds_cli_context * context ,
TALLOC_CTX * mem_ctx ,
struct netlogon_creds_CredentialState * * _creds )
{
NTSTATUS status ;
2017-09-13 21:40:24 +03:00
struct netlogon_creds_CredentialState * creds ;
2013-04-18 21:16:42 +04:00
* _creds = NULL ;
2017-09-13 21:40:24 +03:00
status = netlogon_creds_cli_get_internal ( context , mem_ctx , & creds ) ;
2013-04-18 21:16:42 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/*
* mark it as invalid for step operations .
*/
2017-09-13 21:40:24 +03:00
creds - > sequence = 0 ;
creds - > seed = ( struct netr_Credential ) { { 0 } } ;
creds - > client = ( struct netr_Credential ) { { 0 } } ;
creds - > server = ( struct netr_Credential ) { { 0 } } ;
2013-04-18 21:16:42 +04:00
2017-09-13 21:40:24 +03:00
* _creds = creds ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_OK ;
}
bool netlogon_creds_cli_validate ( struct netlogon_creds_cli_context * context ,
const struct netlogon_creds_CredentialState * creds1 )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct netlogon_creds_CredentialState * creds2 ;
DATA_BLOB blob1 ;
DATA_BLOB blob2 ;
NTSTATUS status ;
enum ndr_err_code ndr_err ;
2022-05-11 02:39:14 +03:00
bool equal ;
2013-04-18 21:16:42 +04:00
status = netlogon_creds_cli_get ( context , frame , & creds2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( frame ) ;
return false ;
}
ndr_err = ndr_push_struct_blob ( & blob1 , frame , creds1 ,
( ndr_push_flags_fn_t ) ndr_push_netlogon_creds_CredentialState ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return false ;
}
ndr_err = ndr_push_struct_blob ( & blob2 , frame , creds2 ,
( ndr_push_flags_fn_t ) ndr_push_netlogon_creds_CredentialState ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
TALLOC_FREE ( frame ) ;
return false ;
}
2022-05-11 02:39:14 +03:00
equal = data_blob_equal_const_time ( & blob1 , & blob2 ) ;
2013-04-18 21:16:42 +04:00
TALLOC_FREE ( frame ) ;
2017-08-21 13:00:23 +03:00
2022-05-11 02:39:14 +03:00
return equal ;
2013-04-18 21:16:42 +04:00
}
2017-09-13 19:32:36 +03:00
static NTSTATUS netlogon_creds_cli_store_internal (
struct netlogon_creds_cli_context * context ,
struct netlogon_creds_CredentialState * creds )
2013-04-18 21:16:42 +04:00
{
2024-10-10 14:24:37 +03:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2013-04-18 21:16:42 +04:00
NTSTATUS status ;
enum ndr_err_code ndr_err ;
DATA_BLOB blob ;
TDB_DATA data ;
2017-09-10 20:11:21 +03:00
if ( DEBUGLEVEL > = 10 ) {
NDR_PRINT_DEBUG ( netlogon_creds_CredentialState , creds ) ;
}
2024-10-10 14:24:37 +03:00
ndr_err = ndr_push_struct_blob ( & blob , frame , creds ,
2013-04-18 21:16:42 +04:00
( ndr_push_flags_fn_t ) ndr_push_netlogon_creds_CredentialState ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
status = ndr_map_error2ntstatus ( ndr_err ) ;
2024-10-10 14:24:37 +03:00
TALLOC_FREE ( frame ) ;
2013-04-18 21:16:42 +04:00
return status ;
}
data . dptr = blob . data ;
data . dsize = blob . length ;
status = dbwrap_store ( context - > db . ctx ,
context - > db . key_data ,
data , TDB_REPLACE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2024-10-10 14:24:37 +03:00
TALLOC_FREE ( frame ) ;
2013-04-18 21:16:42 +04:00
return status ;
}
2024-10-10 14:24:37 +03:00
TALLOC_FREE ( frame ) ;
2013-04-18 21:16:42 +04:00
return NT_STATUS_OK ;
}
2017-09-13 19:32:36 +03:00
NTSTATUS netlogon_creds_cli_store ( struct netlogon_creds_cli_context * context ,
struct netlogon_creds_CredentialState * creds )
{
NTSTATUS status ;
if ( context - > db . locked_state = = NULL ) {
/*
* this was not the result of netlogon_creds_cli_lock * ( )
*/
return NT_STATUS_INVALID_PAGE_PROTECTION ;
}
if ( context - > db . locked_state - > creds ! = creds ) {
/*
* this was not the result of netlogon_creds_cli_lock * ( )
*/
return NT_STATUS_INVALID_PAGE_PROTECTION ;
}
status = netlogon_creds_cli_store_internal ( context , creds ) ;
return status ;
}
2017-09-13 19:33:56 +03:00
static NTSTATUS netlogon_creds_cli_delete_internal (
struct netlogon_creds_cli_context * context )
{
NTSTATUS status ;
status = dbwrap_delete ( context - > db . ctx , context - > db . key_data ) ;
return status ;
}
2017-09-16 05:39:01 +03:00
NTSTATUS netlogon_creds_cli_delete_lck (
struct netlogon_creds_cli_context * context )
{
NTSTATUS status ;
if ( context - > db . lock ! = NETLOGON_CREDS_CLI_LCK_EXCLUSIVE ) {
return NT_STATUS_NOT_LOCKED ;
}
status = netlogon_creds_cli_delete_internal ( context ) ;
return status ;
}
2013-04-18 21:16:42 +04:00
NTSTATUS netlogon_creds_cli_delete ( struct netlogon_creds_cli_context * context ,
2017-09-10 15:55:13 +03:00
struct netlogon_creds_CredentialState * creds )
2013-04-18 21:16:42 +04:00
{
NTSTATUS status ;
if ( context - > db . locked_state = = NULL ) {
/*
* this was not the result of netlogon_creds_cli_lock * ( )
*/
return NT_STATUS_INVALID_PAGE_PROTECTION ;
}
if ( context - > db . locked_state - > creds ! = creds ) {
/*
* this was not the result of netlogon_creds_cli_lock * ( )
*/
return NT_STATUS_INVALID_PAGE_PROTECTION ;
}
2017-09-13 19:33:56 +03:00
status = netlogon_creds_cli_delete_internal ( context ) ;
return status ;
2013-04-18 21:16:42 +04:00
}
struct netlogon_creds_cli_lock_state {
struct netlogon_creds_cli_locked_state * locked_state ;
struct netlogon_creds_CredentialState * creds ;
} ;
static void netlogon_creds_cli_lock_done ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_lock_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context )
{
struct tevent_req * req ;
struct netlogon_creds_cli_lock_state * state ;
struct netlogon_creds_cli_locked_state * locked_state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_lock_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( context - > db . locked_state ! = NULL ) {
tevent_req_nterror ( req , NT_STATUS_LOCK_NOT_GRANTED ) ;
return tevent_req_post ( req , ev ) ;
}
locked_state = talloc_zero ( state , struct netlogon_creds_cli_locked_state ) ;
if ( tevent_req_nomem ( locked_state , req ) ) {
return tevent_req_post ( req , ev ) ;
}
talloc_set_destructor ( locked_state ,
netlogon_creds_cli_locked_state_destructor ) ;
locked_state - > context = context ;
context - > db . locked_state = locked_state ;
state - > locked_state = locked_state ;
if ( context - > db . g_ctx = = NULL ) {
2017-09-13 18:51:25 +03:00
NTSTATUS status ;
2017-09-13 21:38:11 +03:00
status = netlogon_creds_cli_get_internal (
2017-09-13 18:51:25 +03:00
context , state , & state - > creds ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
return tevent_req_post ( req , ev ) ;
}
return req ;
}
subreq = g_lock_lock_send ( state , ev ,
context - > db . g_ctx ,
2017-12-03 22:47:02 +03:00
string_term_tdb_data ( context - > db . key_name ) ,
2022-08-28 13:38:24 +03:00
G_LOCK_WRITE ,
NULL , NULL ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , netlogon_creds_cli_lock_done , req ) ;
return req ;
}
static void netlogon_creds_cli_lock_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_lock_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_lock_state ) ;
NTSTATUS status ;
status = g_lock_lock_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
state - > locked_state - > is_glocked = true ;
2017-09-13 21:38:11 +03:00
status = netlogon_creds_cli_get_internal ( state - > locked_state - > context ,
2017-09-13 18:51:25 +03:00
state , & state - > creds ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
tevent_req_done ( req ) ;
2013-04-18 21:16:42 +04:00
}
2017-09-13 21:38:11 +03:00
static NTSTATUS netlogon_creds_cli_get_internal (
2017-09-13 18:51:25 +03:00
struct netlogon_creds_cli_context * context ,
TALLOC_CTX * mem_ctx , struct netlogon_creds_CredentialState * * pcreds )
2013-04-18 21:16:42 +04:00
{
struct netlogon_creds_cli_fetch_state fstate = {
. status = NT_STATUS_INTERNAL_ERROR ,
2021-11-18 15:46:26 +03:00
. proposed_flags = context - > client . proposed_flags ,
2013-04-18 21:16:42 +04:00
. required_flags = context - > client . required_flags ,
} ;
NTSTATUS status ;
2017-09-13 18:51:25 +03:00
fstate . mem_ctx = mem_ctx ;
2013-04-18 21:16:42 +04:00
status = dbwrap_parse_record ( context - > db . ctx ,
context - > db . key_data ,
netlogon_creds_cli_fetch_parser ,
& fstate ) ;
2017-09-13 18:51:25 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2013-04-18 21:16:42 +04:00
}
2017-09-13 18:51:25 +03:00
if ( ! NT_STATUS_IS_OK ( fstate . status ) ) {
return fstate . status ;
2013-04-18 21:16:42 +04:00
}
if ( context - > server . cached_flags = = fstate . creds - > negotiate_flags ) {
2017-09-13 18:51:25 +03:00
* pcreds = fstate . creds ;
return NT_STATUS_OK ;
2013-04-18 21:16:42 +04:00
}
2017-09-13 21:37:00 +03:00
/*
* It is really important to try SamLogonEx here ,
* because multiple processes can talk to the same
* domain controller , without using the credential
* chain .
*
* With a normal SamLogon call , we must keep the
* credentials chain updated and intact between all
* users of the machine account ( which would imply
* cross - node communication for every NTLM logon ) .
*
* The credentials chain is not per NETLOGON pipe
* connection , but globally on the server / client pair
* by computer name .
*
* It ' s also important to use NetlogonValidationSamInfo4 ( 6 ) ,
* because it relies on the rpc transport encryption
* and avoids using the global netlogon schannel
* session key to en / decrypt secret information
* like the user_session_key for network logons .
*
* [ MS - APDS ] 3.1 .5 .2 NTLM Network Logon
* says NETLOGON_NEG_CROSS_FOREST_TRUSTS and
* NETLOGON_NEG_AUTHENTICATED_RPC set together
* are the indication that the server supports
* NetlogonValidationSamInfo4 ( 6 ) . And it must only
* be used if " SealSecureChannel " is used .
*
* The " SealSecureChannel " AUTH_TYPE_SCHANNEL / AUTH_LEVEL_PRIVACY
* check is done in netlogon_creds_cli_LogonSamLogon * ( ) .
*/
2013-04-18 21:16:42 +04:00
context - > server . cached_flags = fstate . creds - > negotiate_flags ;
context - > server . try_validation6 = true ;
context - > server . try_logon_ex = true ;
context - > server . try_logon_with = true ;
if ( ! ( context - > server . cached_flags & NETLOGON_NEG_AUTHENTICATED_RPC ) ) {
context - > server . try_validation6 = false ;
context - > server . try_logon_ex = false ;
}
if ( ! ( context - > server . cached_flags & NETLOGON_NEG_CROSS_FOREST_TRUSTS ) ) {
context - > server . try_validation6 = false ;
}
2017-09-13 18:51:25 +03:00
* pcreds = fstate . creds ;
return NT_STATUS_OK ;
2013-04-18 21:16:42 +04:00
}
NTSTATUS netlogon_creds_cli_lock_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct netlogon_creds_CredentialState * * creds )
{
struct netlogon_creds_cli_lock_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_lock_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
talloc_steal ( state - > creds , state - > locked_state ) ;
state - > locked_state - > creds = state - > creds ;
* creds = talloc_move ( mem_ctx , & state - > creds ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_lock ( struct netlogon_creds_cli_context * context ,
TALLOC_CTX * mem_ctx ,
struct netlogon_creds_CredentialState * * creds )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_lock_send ( frame , ev , context ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_lock_recv ( req , mem_ctx , creds ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2017-09-12 02:48:27 +03:00
struct netlogon_creds_cli_lck {
struct netlogon_creds_cli_context * context ;
} ;
struct netlogon_creds_cli_lck_state {
struct netlogon_creds_cli_lck * lck ;
enum netlogon_creds_cli_lck_type type ;
} ;
static void netlogon_creds_cli_lck_locked ( struct tevent_req * subreq ) ;
static int netlogon_creds_cli_lck_destructor (
struct netlogon_creds_cli_lck * lck ) ;
struct tevent_req * netlogon_creds_cli_lck_send (
TALLOC_CTX * mem_ctx , struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
enum netlogon_creds_cli_lck_type type )
{
struct tevent_req * req , * subreq ;
struct netlogon_creds_cli_lck_state * state ;
enum g_lock_type gtype ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_lck_state ) ;
if ( req = = NULL ) {
return NULL ;
}
if ( context - > db . lock ! = NETLOGON_CREDS_CLI_LCK_NONE ) {
DBG_DEBUG ( " context already locked \n " ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_LOCK_SEQUENCE ) ;
return tevent_req_post ( req , ev ) ;
}
switch ( type ) {
case NETLOGON_CREDS_CLI_LCK_SHARED :
gtype = G_LOCK_READ ;
break ;
case NETLOGON_CREDS_CLI_LCK_EXCLUSIVE :
gtype = G_LOCK_WRITE ;
break ;
default :
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
state - > lck = talloc ( state , struct netlogon_creds_cli_lck ) ;
if ( tevent_req_nomem ( state - > lck , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > lck - > context = context ;
state - > type = type ;
subreq = g_lock_lock_send ( state , ev ,
context - > db . g_ctx ,
2017-12-03 22:47:02 +03:00
string_term_tdb_data ( context - > db . key_name ) ,
2022-08-28 13:38:24 +03:00
gtype ,
NULL , NULL ) ;
2017-09-12 02:48:27 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , netlogon_creds_cli_lck_locked , req ) ;
return req ;
}
static void netlogon_creds_cli_lck_locked ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct netlogon_creds_cli_lck_state * state = tevent_req_data (
req , struct netlogon_creds_cli_lck_state ) ;
NTSTATUS status ;
status = g_lock_lock_recv ( subreq ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
state - > lck - > context - > db . lock = state - > type ;
talloc_set_destructor ( state - > lck , netlogon_creds_cli_lck_destructor ) ;
tevent_req_done ( req ) ;
}
static int netlogon_creds_cli_lck_destructor (
struct netlogon_creds_cli_lck * lck )
{
struct netlogon_creds_cli_context * ctx = lck - > context ;
NTSTATUS status ;
2017-12-03 22:47:02 +03:00
status = g_lock_unlock ( ctx - > db . g_ctx ,
string_term_tdb_data ( ctx - > db . key_name ) ) ;
2017-09-12 02:48:27 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " g_lock_unlock failed: %s \n " , nt_errstr ( status ) ) ;
smb_panic ( " g_lock_unlock failed " ) ;
}
ctx - > db . lock = NETLOGON_CREDS_CLI_LCK_NONE ;
return 0 ;
}
NTSTATUS netlogon_creds_cli_lck_recv (
struct tevent_req * req , TALLOC_CTX * mem_ctx ,
struct netlogon_creds_cli_lck * * lck )
{
struct netlogon_creds_cli_lck_state * state = tevent_req_data (
req , struct netlogon_creds_cli_lck_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* lck = talloc_move ( mem_ctx , & state - > lck ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_lck (
struct netlogon_creds_cli_context * context ,
enum netlogon_creds_cli_lck_type type ,
TALLOC_CTX * mem_ctx , struct netlogon_creds_cli_lck * * lck )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_lck_send ( frame , ev , context , type ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_lck_recv ( req , mem_ctx , lck ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2024-10-29 15:42:06 +03:00
static NTSTATUS netlogon_creds_cli_check_transport (
enum dcerpc_AuthType auth_type ,
enum dcerpc_AuthLevel auth_level ,
const struct netlogon_creds_CredentialState * creds ,
enum dcerpc_AuthLevel min_auth_level )
{
if ( auth_level < min_auth_level ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( creds = = NULL ) {
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( auth_type = = DCERPC_AUTH_TYPE_SCHANNEL ) {
switch ( auth_level ) {
case DCERPC_AUTH_LEVEL_INTEGRITY :
case DCERPC_AUTH_LEVEL_PRIVACY :
return NT_STATUS_OK ;
default :
break ;
}
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
if ( creds - > negotiate_flags & NETLOGON_NEG_AUTHENTICATED_RPC ) {
/*
* if DCERPC_AUTH_TYPE_SCHANNEL is supported
* it should be used , which means
* we had a chance to verify no downgrade
* happened .
*
* This relies on netlogon_creds_cli_check *
* being called before , as first request after
* the DCERPC bind .
*/
return NT_STATUS_INVALID_PARAMETER_MIX ;
}
return NT_STATUS_OK ;
}
2013-04-18 21:16:42 +04:00
struct netlogon_creds_cli_auth_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
2024-10-29 12:02:40 +03:00
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
2017-05-22 21:44:40 +03:00
uint8_t num_nt_hashes ;
uint8_t idx_nt_hashes ;
const struct samr_Password * const * nt_hashes ;
const struct samr_Password * used_nt_hash ;
2013-04-18 21:16:42 +04:00
char * srv_name_slash ;
uint32_t current_flags ;
struct netr_Credential client_challenge ;
struct netr_Credential server_challenge ;
struct netlogon_creds_CredentialState * creds ;
struct netr_Credential client_credential ;
struct netr_Credential server_credential ;
2024-10-02 14:43:36 +03:00
uint32_t negotiate_flags ;
2013-04-18 21:16:42 +04:00
uint32_t rid ;
bool try_auth3 ;
bool try_auth2 ;
bool require_auth2 ;
} ;
static void netlogon_creds_cli_auth_challenge_start ( struct tevent_req * req ) ;
struct tevent_req * netlogon_creds_cli_auth_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
2017-05-22 21:44:40 +03:00
uint8_t num_nt_hashes ,
const struct samr_Password * const * nt_hashes )
2013-04-18 21:16:42 +04:00
{
struct tevent_req * req ;
struct netlogon_creds_cli_auth_state * state ;
NTSTATUS status ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_auth_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
2017-05-22 21:44:40 +03:00
if ( num_nt_hashes < 1 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
if ( num_nt_hashes > 4 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
2013-04-18 21:16:42 +04:00
}
2017-05-22 21:44:40 +03:00
state - > num_nt_hashes = num_nt_hashes ;
state - > idx_nt_hashes = 0 ;
state - > nt_hashes = nt_hashes ;
2017-09-13 21:51:47 +03:00
if ( context - > db . lock ! = NETLOGON_CREDS_CLI_LCK_EXCLUSIVE ) {
tevent_req_nterror ( req , NT_STATUS_NOT_LOCKED ) ;
2013-04-18 21:16:42 +04:00
return tevent_req_post ( req , ev ) ;
}
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2024-10-29 12:02:40 +03:00
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
2013-04-18 21:16:42 +04:00
state - > try_auth3 = true ;
state - > try_auth2 = true ;
if ( context - > client . required_flags ! = 0 ) {
state - > require_auth2 = true ;
}
2017-05-22 21:44:40 +03:00
state - > used_nt_hash = state - > nt_hashes [ state - > idx_nt_hashes ] ;
2013-04-18 21:16:42 +04:00
state - > current_flags = context - > client . proposed_flags ;
2016-02-25 18:15:04 +03:00
status = dbwrap_purge ( state - > context - > db . ctx ,
state - > context - > db . key_data ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
netlogon_creds_cli_auth_challenge_start ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
}
return req ;
}
static void netlogon_creds_cli_auth_challenge_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_auth_challenge_start ( struct tevent_req * req )
{
struct netlogon_creds_cli_auth_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_auth_state ) ;
struct tevent_req * subreq ;
TALLOC_FREE ( state - > creds ) ;
2020-09-16 17:08:38 +03:00
netlogon_creds_random_challenge ( & state - > client_challenge ) ;
2013-04-18 21:16:42 +04:00
subreq = dcerpc_netr_ServerReqChallenge_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
& state - > client_challenge ,
& state - > server_challenge ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_auth_challenge_done ,
req ) ;
}
static void netlogon_creds_cli_auth_srvauth_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_auth_challenge_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_auth_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_auth_state ) ;
NTSTATUS status ;
NTSTATUS result ;
status = dcerpc_netr_ServerReqChallenge_recv ( subreq , state , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
return ;
}
if ( ! state - > try_auth3 & & ! state - > try_auth2 ) {
state - > current_flags = 0 ;
}
/* Calculate the session key and client credentials */
state - > creds = netlogon_creds_client_init ( state ,
state - > context - > client . account ,
state - > context - > client . computer ,
state - > context - > client . type ,
& state - > client_challenge ,
& state - > server_challenge ,
2017-05-22 21:44:40 +03:00
state - > used_nt_hash ,
2013-04-18 21:16:42 +04:00
& state - > client_credential ,
2024-10-02 20:06:59 +03:00
state - > context - > client . proposed_flags ,
2013-04-18 21:16:42 +04:00
state - > current_flags ) ;
if ( tevent_req_nomem ( state - > creds , req ) ) {
return ;
}
if ( state - > try_auth3 ) {
2024-10-02 14:43:36 +03:00
/*
* We always need to send our proposed flags ,
* even if state - > current_flags we passed to
* netlogon_creds_client_init ( ) is already downgraded ,
*
* An old server will just ignore the bits it doesn ' t
* know about , but LogonGetCapabilities ( level = 2 ) will
* report what we proposed .
*/
state - > negotiate_flags = state - > context - > client . proposed_flags ;
2013-04-18 21:16:42 +04:00
subreq = dcerpc_netr_ServerAuthenticate3_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . account ,
state - > context - > client . type ,
state - > context - > client . computer ,
& state - > client_credential ,
& state - > server_credential ,
2024-10-02 14:43:36 +03:00
& state - > negotiate_flags ,
2013-04-18 21:16:42 +04:00
& state - > rid ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
} else if ( state - > try_auth2 ) {
2024-10-02 14:43:36 +03:00
/*
* We always need to send our proposed flags ,
* even if state - > current_flags we passed to
* netlogon_creds_client_init ( ) is already downgraded ,
*
* An old server will just ignore the bits it doesn ' t
* know about , but LogonGetCapabilities ( level = 2 ) will
* report what we proposed .
*/
state - > negotiate_flags = state - > context - > client . proposed_flags ;
2013-04-18 21:16:42 +04:00
state - > rid = 0 ;
subreq = dcerpc_netr_ServerAuthenticate2_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . account ,
state - > context - > client . type ,
state - > context - > client . computer ,
& state - > client_credential ,
& state - > server_credential ,
2024-10-02 14:43:36 +03:00
& state - > negotiate_flags ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
} else {
2024-10-02 14:43:36 +03:00
state - > negotiate_flags = 0 ;
2013-04-18 21:16:42 +04:00
state - > rid = 0 ;
subreq = dcerpc_netr_ServerAuthenticate_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . account ,
state - > context - > client . type ,
state - > context - > client . computer ,
& state - > client_credential ,
& state - > server_credential ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_auth_srvauth_done ,
req ) ;
}
static void netlogon_creds_cli_auth_srvauth_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_auth_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_auth_state ) ;
NTSTATUS status ;
NTSTATUS result ;
2021-11-18 15:46:26 +03:00
bool downgraded ;
2013-04-18 21:16:42 +04:00
if ( state - > try_auth3 ) {
status = dcerpc_netr_ServerAuthenticate3_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
state - > try_auth3 = false ;
netlogon_creds_cli_auth_challenge_start ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
} else if ( state - > try_auth2 ) {
status = dcerpc_netr_ServerAuthenticate2_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
state - > try_auth2 = false ;
if ( state - > require_auth2 ) {
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
return ;
}
netlogon_creds_cli_auth_challenge_start ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
} else {
status = dcerpc_netr_ServerAuthenticate_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
}
if ( ! NT_STATUS_IS_OK ( result ) & &
! NT_STATUS_EQUAL ( result , NT_STATUS_ACCESS_DENIED ) )
{
tevent_req_nterror ( req , result ) ;
return ;
}
2021-11-18 15:46:26 +03:00
downgraded = netlogon_creds_cli_downgraded (
2024-10-02 14:43:36 +03:00
state - > negotiate_flags ,
2021-11-18 15:46:26 +03:00
state - > context - > client . proposed_flags ,
state - > context - > client . required_flags ) ;
if ( downgraded ) {
2013-04-18 21:16:42 +04:00
if ( NT_STATUS_IS_OK ( result ) ) {
tevent_req_nterror ( req , NT_STATUS_DOWNGRADE_DETECTED ) ;
return ;
}
tevent_req_nterror ( req , result ) ;
return ;
}
if ( NT_STATUS_EQUAL ( result , NT_STATUS_ACCESS_DENIED ) ) {
2024-10-02 16:03:21 +03:00
uint32_t prop_f = state - > context - > client . proposed_flags ;
uint32_t cli_f = state - > current_flags ;
2024-10-02 14:43:36 +03:00
uint32_t srv_f = state - > negotiate_flags ;
2024-10-02 16:03:21 +03:00
uint32_t nego_f = cli_f & srv_f ;
if ( cli_f = = prop_f & & nego_f ! = prop_f ) {
2013-04-18 21:16:42 +04:00
/*
* lets retry with the negotiated flags
*/
2024-10-02 16:03:21 +03:00
state - > current_flags = nego_f ;
2013-04-18 21:16:42 +04:00
netlogon_creds_cli_auth_challenge_start ( req ) ;
return ;
}
2017-05-22 21:44:40 +03:00
state - > idx_nt_hashes + = 1 ;
if ( state - > idx_nt_hashes > = state - > num_nt_hashes ) {
2013-04-18 21:16:42 +04:00
/*
* we already retried , giving up . . .
*/
tevent_req_nterror ( req , result ) ;
return ;
}
/*
* lets retry with the old nt hash .
*/
2017-05-22 21:44:40 +03:00
state - > used_nt_hash = state - > nt_hashes [ state - > idx_nt_hashes ] ;
2013-04-18 21:16:42 +04:00
state - > current_flags = state - > context - > client . proposed_flags ;
netlogon_creds_cli_auth_challenge_start ( req ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( state - > creds ,
& state - > server_credential ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
return ;
}
2024-10-02 14:43:36 +03:00
if ( state - > current_flags = = state - > context - > client . proposed_flags ) {
/*
* Without a downgrade in the crypto we proposed
* we can adjust the otherwise downgraded flags
* before storing .
*/
state - > creds - > negotiate_flags & = state - > negotiate_flags ;
} else if ( state - > current_flags ! = state - > negotiate_flags ) {
/*
* We downgraded our crypto once , we should not
* allow any additional downgrade !
*/
tevent_req_nterror ( req , NT_STATUS_DOWNGRADE_DETECTED ) ;
return ;
}
2024-11-15 18:24:25 +03:00
state - > creds - > client_sid . sub_auths [ 0 ] = state - > rid ;
2023-07-19 18:43:00 +03:00
status = netlogon_creds_cli_store_internal ( state - > context ,
state - > creds ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
tevent_req_done ( req ) ;
}
2017-05-22 21:44:40 +03:00
NTSTATUS netlogon_creds_cli_auth_recv ( struct tevent_req * req ,
uint8_t * idx_nt_hashes )
2013-04-18 21:16:42 +04:00
{
2017-05-22 21:44:40 +03:00
struct netlogon_creds_cli_auth_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_auth_state ) ;
2013-04-18 21:16:42 +04:00
NTSTATUS status ;
2017-05-22 21:44:40 +03:00
* idx_nt_hashes = 0 ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
2017-05-22 21:44:40 +03:00
* idx_nt_hashes = state - > idx_nt_hashes ;
2013-04-18 21:16:42 +04:00
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_auth ( struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
2017-05-22 21:44:40 +03:00
uint8_t num_nt_hashes ,
const struct samr_Password * const * nt_hashes ,
uint8_t * idx_nt_hashes )
2013-04-18 21:16:42 +04:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2017-05-22 21:44:40 +03:00
* idx_nt_hashes = 0 ;
2013-04-18 21:16:42 +04:00
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_auth_send ( frame , ev , context , b ,
2017-05-22 21:44:40 +03:00
num_nt_hashes , nt_hashes ) ;
2013-04-18 21:16:42 +04:00
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2017-05-22 21:44:40 +03:00
status = netlogon_creds_cli_auth_recv ( req , idx_nt_hashes ) ;
2013-04-18 21:16:42 +04:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
struct netlogon_creds_cli_check_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
2024-10-29 12:02:40 +03:00
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
2013-04-18 21:16:42 +04:00
char * srv_name_slash ;
union netr_Capabilities caps ;
2024-10-02 14:43:36 +03:00
union netr_Capabilities client_caps ;
2013-04-18 21:16:42 +04:00
struct netlogon_creds_CredentialState * creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
2024-10-02 15:25:19 +03:00
union netr_CONTROL_QUERY_INFORMATION ctrl_info ;
2013-04-18 21:16:42 +04:00
} ;
static void netlogon_creds_cli_check_cleanup ( struct tevent_req * req ,
2014-01-08 15:04:22 +04:00
NTSTATUS status ) ;
2024-10-02 14:43:36 +03:00
static void netlogon_creds_cli_check_negotiate_caps ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_check_client_caps ( struct tevent_req * subreq ) ;
2013-04-18 21:16:42 +04:00
struct tevent_req * netlogon_creds_cli_check_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b )
{
struct tevent_req * req ;
struct netlogon_creds_cli_check_state * state ;
struct tevent_req * subreq ;
2017-09-13 19:40:57 +03:00
NTSTATUS status ;
2013-04-18 21:16:42 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_check_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
2017-09-13 19:40:57 +03:00
if ( context - > db . lock ! = NETLOGON_CREDS_CLI_LCK_EXCLUSIVE ) {
tevent_req_nterror ( req , NT_STATUS_NOT_LOCKED ) ;
return tevent_req_post ( req , ev ) ;
}
status = netlogon_creds_cli_get_internal ( context , state ,
& state - > creds ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2013-04-18 21:16:42 +04:00
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
2024-10-29 12:02:40 +03:00
& state - > auth_type ,
& state - > auth_level ) ;
2013-04-18 21:16:42 +04:00
2024-10-29 15:42:06 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_INTEGRITY ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
return tevent_req_post ( req , ev ) ;
}
2017-09-13 19:40:57 +03:00
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( state - > creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return tevent_req_post ( req , ev ) ;
}
2017-09-13 19:40:57 +03:00
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_LogonGetCapabilities_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
& state - > req_auth ,
& state - > rep_auth ,
1 ,
& state - > caps ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
2024-10-02 14:43:36 +03:00
netlogon_creds_cli_check_negotiate_caps ,
2013-04-18 21:16:42 +04:00
req ) ;
return req ;
}
static void netlogon_creds_cli_check_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_check_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_check_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-13 19:40:57 +03:00
netlogon_creds_cli_delete_lck ( state - > context ) ;
2017-09-10 15:55:13 +03:00
TALLOC_FREE ( state - > creds ) ;
2013-04-18 21:16:42 +04:00
}
2024-10-02 15:25:19 +03:00
static void netlogon_creds_cli_check_control_do ( struct tevent_req * req ) ;
2024-10-02 14:43:36 +03:00
static void netlogon_creds_cli_check_negotiate_caps ( struct tevent_req * subreq )
2013-04-18 21:16:42 +04:00
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_check_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_check_state ) ;
NTSTATUS status ;
NTSTATUS result ;
status = dcerpc_netr_LogonGetCapabilities_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
/*
* Note that the negotiated flags are already checked
* for our required flags after the ServerAuthenticate3 / 2 call .
*/
2017-09-13 19:40:57 +03:00
uint32_t negotiated = state - > creds - > negotiate_flags ;
2013-04-18 21:16:42 +04:00
if ( negotiated & NETLOGON_NEG_SUPPORTS_AES ) {
/*
* If we have negotiated NETLOGON_NEG_SUPPORTS_AES
* already , we expect this to work !
*/
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
if ( negotiated & NETLOGON_NEG_STRONG_KEYS ) {
/*
* If we have negotiated NETLOGON_NEG_STRONG_KEYS
* we expect this to work at least as far as the
* NOT_SUPPORTED error handled below !
*
* NT 4.0 and Old Samba servers are not
* allowed without " require strong key = no "
*/
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
/*
* If we not require NETLOGON_NEG_SUPPORTS_AES or
* NETLOGON_NEG_STRONG_KEYS , it ' s ok to ignore
* NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE .
*
* This is needed against NT 4.0 and old Samba servers .
*
* As we ' re using DCERPC_AUTH_TYPE_SCHANNEL with
* DCERPC_AUTH_LEVEL_INTEGRITY or DCERPC_AUTH_LEVEL_PRIVACY
* we should detect a faked NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE
* with the next request as the sequence number processing
* gets out of sync .
2024-10-02 15:25:19 +03:00
*
* So we ' ll do a LogonControl message to check that . . .
2013-04-18 21:16:42 +04:00
*/
2024-10-02 15:25:19 +03:00
netlogon_creds_cli_check_control_do ( req ) ;
2013-04-18 21:16:42 +04:00
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
if ( NT_STATUS_EQUAL ( result , NT_STATUS_NOT_IMPLEMENTED ) ) {
/*
* Note that the negotiated flags are already checked
* for our required flags after the ServerAuthenticate3 / 2 call .
*/
2017-09-13 19:40:57 +03:00
uint32_t negotiated = state - > creds - > negotiate_flags ;
2013-04-18 21:16:42 +04:00
if ( negotiated & NETLOGON_NEG_SUPPORTS_AES ) {
/*
* If we have negotiated NETLOGON_NEG_SUPPORTS_AES
* already , we expect this to work !
*/
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
/*
* This is ok , the server does not support
* NETLOGON_NEG_SUPPORTS_AES .
*
* netr_LogonGetCapabilities ( ) was
* netr_LogonDummyRoutine1 ( ) before
* NETLOGON_NEG_SUPPORTS_AES was invented .
*/
netlogon_creds_cli_check_cleanup ( req , result ) ;
tevent_req_done ( req ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( state - > creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_check_cleanup ( req , result ) ;
return ;
}
2017-09-13 19:40:57 +03:00
if ( state - > caps . server_capabilities ! = state - > creds - > negotiate_flags ) {
2013-04-18 21:16:42 +04:00
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
/*
* This is the key check that makes this check secure . If we
* get OK here ( rather than NOT_SUPPORTED ) , then the server
* did support AES . If the server only proposed STRONG_KEYS
* and not AES , then it should have failed with
* NOT_IMPLEMENTED . We always send AES as a client , so the
* server should always have returned it .
*/
if ( ! ( state - > caps . server_capabilities & NETLOGON_NEG_SUPPORTS_AES ) ) {
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
2017-09-13 19:40:57 +03:00
status = netlogon_creds_cli_store_internal ( state - > context ,
state - > creds ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-10-02 14:43:36 +03:00
/*
* Now try to verify our client proposed flags
* arrived at the server , using query_level = 2
*/
status = netlogon_creds_client_authenticator ( state - > creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_LogonGetCapabilities_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
& state - > req_auth ,
& state - > rep_auth ,
2 ,
& state - > client_caps ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_check_client_caps ,
req ) ;
return ;
}
static void netlogon_creds_cli_check_client_caps ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_check_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_check_state ) ;
2024-10-02 20:06:59 +03:00
uint32_t requested_flags ;
2024-10-02 14:43:36 +03:00
NTSTATUS status ;
NTSTATUS result ;
status = dcerpc_netr_LogonGetCapabilities_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_BAD_STUB_DATA ) ) {
/*
* unpatched Samba server , see
* https : //bugzilla.samba.org/show_bug.cgi?id=15418
*/
status = NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE ) ) {
/*
* Here we know the negotiated flags were already
* verified with query_level = 1 , which means
* the server supported NETLOGON_NEG_SUPPORTS_AES
* and also NETLOGON_NEG_AUTHENTICATED_RPC
*
* As we ' re using DCERPC_AUTH_TYPE_SCHANNEL with
* DCERPC_AUTH_LEVEL_INTEGRITY or DCERPC_AUTH_LEVEL_PRIVACY
* we should detect a faked
* NT_STATUS_RPC_ENUM_VALUE_OUT_OF_RANGE
* with the next request as the sequence number processing
* gets out of sync .
*
* So we ' ll do a LogonControl message to check that . . .
*/
netlogon_creds_cli_check_control_do ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( state - > creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2024-10-02 14:43:36 +03:00
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_check_cleanup ( req , result ) ;
return ;
}
2024-11-15 18:24:25 +03:00
requested_flags = state - > creds - > client_requested_flags ;
2024-10-02 20:06:59 +03:00
if ( state - > client_caps . requested_flags ! = requested_flags ) {
2024-10-02 14:43:36 +03:00
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
status = netlogon_creds_cli_store_internal ( state - > context ,
state - > creds ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2013-04-18 21:16:42 +04:00
tevent_req_done ( req ) ;
}
2024-10-02 15:25:19 +03:00
static void netlogon_creds_cli_check_control_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_check_control_do ( struct tevent_req * req )
{
struct netlogon_creds_cli_check_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_check_state ) ;
struct tevent_req * subreq = NULL ;
/*
* In case we got a downgrade based on a FAULT
* we use a LogonControl that is supposed to
* return WERR_NOT_SUPPORTED ( without a DCERPC FAULT )
* to verify that the connection is still ok and didn ' t
* get out of sync .
*/
subreq = dcerpc_netr_LogonControl_send ( state ,
state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
NETLOGON_CONTROL_QUERY ,
2 ,
& state - > ctrl_info ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_check_control_done ,
req ) ;
return ;
}
static void netlogon_creds_cli_check_control_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_check_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_check_state ) ;
NTSTATUS status ;
WERROR result ;
status = dcerpc_netr_LogonControl_recv ( subreq , state , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
/*
* We want to delete the creds ,
* so we pass NT_STATUS_DOWNGRADE_DETECTED
* to netlogon_creds_cli_check_cleanup ( )
*/
status = NT_STATUS_DOWNGRADE_DETECTED ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
if ( ! W_ERROR_EQUAL ( result , WERR_NOT_SUPPORTED ) ) {
status = NT_STATUS_DOWNGRADE_DETECTED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_check_cleanup ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
2017-09-20 02:45:27 +03:00
NTSTATUS netlogon_creds_cli_check_recv ( struct tevent_req * req ,
union netr_Capabilities * capabilities )
2013-04-18 21:16:42 +04:00
{
2017-09-20 02:45:27 +03:00
struct netlogon_creds_cli_check_state * state = tevent_req_data (
req , struct netlogon_creds_cli_check_state ) ;
2013-04-18 21:16:42 +04:00
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_check_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
2017-09-20 02:45:27 +03:00
if ( capabilities ! = NULL ) {
* capabilities = state - > caps ;
}
2013-04-18 21:16:42 +04:00
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_check ( struct netlogon_creds_cli_context * context ,
2017-09-20 02:45:27 +03:00
struct dcerpc_binding_handle * b ,
union netr_Capabilities * capabilities )
2013-04-18 21:16:42 +04:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_check_send ( frame , ev , context , b ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2017-09-20 02:45:27 +03:00
status = netlogon_creds_cli_check_recv ( req , capabilities ) ;
2013-04-18 21:16:42 +04:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
struct netlogon_creds_cli_ServerPasswordSet_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
uint32_t old_timeout ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
struct samr_CryptPassword samr_crypt_password ;
struct netr_CryptPassword netr_crypt_password ;
struct samr_Password samr_password ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_ServerPasswordSet_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_ServerPasswordSet_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_ServerPasswordSet_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
2017-06-13 12:18:37 +03:00
const DATA_BLOB * new_password ,
2013-04-18 21:16:42 +04:00
const uint32_t * new_version )
{
struct tevent_req * req ;
struct netlogon_creds_cli_ServerPasswordSet_state * state ;
struct tevent_req * subreq ;
bool ok ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_ServerPasswordSet_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
2017-06-13 12:18:37 +03:00
if ( new_password - > length < 14 ) {
2017-01-18 22:02:21 +03:00
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
2013-04-18 21:16:42 +04:00
2017-06-13 12:18:37 +03:00
/*
* netr_ServerPasswordSet
*/
mdfour ( state - > samr_password . hash , new_password - > data , new_password - > length ) ;
2013-04-18 21:16:42 +04:00
/*
* netr_ServerPasswordSet2
*/
2017-06-13 12:18:37 +03:00
ok = set_pw_in_buffer ( state - > samr_crypt_password . data ,
new_password ) ;
2013-04-18 21:16:42 +04:00
if ( ! ok ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
if ( new_version ! = NULL ) {
struct NL_PASSWORD_VERSION version ;
2014-02-24 12:46:00 +04:00
uint32_t len = IVAL ( state - > samr_crypt_password . data , 512 ) ;
uint32_t ofs = 512 - len ;
2013-04-18 21:16:42 +04:00
uint8_t * p ;
2014-02-13 23:45:23 +04:00
if ( len > 500 ) {
2013-04-18 21:16:42 +04:00
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER_MIX ) ;
return tevent_req_post ( req , ev ) ;
}
ofs - = 12 ;
version . ReservedField = 0 ;
version . PasswordVersionNumber = * new_version ;
version . PasswordVersionPresent =
NETLOGON_PASSWORD_VERSION_NUMBER_PRESENT ;
p = state - > samr_crypt_password . data + ofs ;
SIVAL ( p , 0 , version . ReservedField ) ;
SIVAL ( p , 4 , version . PasswordVersionNumber ) ;
SIVAL ( p , 8 , version . PasswordVersionPresent ) ;
}
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_ServerPasswordSet_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_ServerPasswordSet_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_ServerPasswordSet_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerPasswordSet_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
dcerpc_binding_handle_set_timeout ( state - > binding_handle ,
state - > old_timeout ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2013-04-18 21:16:42 +04:00
}
static void netlogon_creds_cli_ServerPasswordSet_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_ServerPasswordSet_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_ServerPasswordSet_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerPasswordSet_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-10-29 15:42:06 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_NONE ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2013-04-18 21:16:42 +04:00
}
state - > old_timeout = dcerpc_binding_handle_set_timeout (
state - > binding_handle , 600000 ) ;
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2013-04-18 21:16:42 +04:00
ZERO_STRUCT ( state - > rep_auth ) ;
if ( state - > tmp_creds . negotiate_flags & NETLOGON_NEG_PASSWORD_SET2 ) {
2024-10-28 17:56:09 +03:00
status = netlogon_creds_encrypt_samr_CryptPassword ( & state - > tmp_creds ,
& state - > samr_crypt_password ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
2013-04-18 21:16:42 +04:00
}
memcpy ( state - > netr_crypt_password . data ,
state - > samr_crypt_password . data , 512 ) ;
state - > netr_crypt_password . length =
IVAL ( state - > samr_crypt_password . data , 512 ) ;
subreq = dcerpc_netr_ServerPasswordSet2_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . account_name ,
state - > tmp_creds . secure_channel_type ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
& state - > netr_crypt_password ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
} else {
2024-10-28 19:19:09 +03:00
status = netlogon_creds_encrypt_samr_Password ( & state - > tmp_creds ,
& state - > samr_password ,
state - > auth_type ,
state - > auth_level ) ;
2019-11-20 18:02:16 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
2013-04-18 21:16:42 +04:00
subreq = dcerpc_netr_ServerPasswordSet_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . account_name ,
state - > tmp_creds . secure_channel_type ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
& state - > samr_password ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_ServerPasswordSet_done ,
req ) ;
}
static void netlogon_creds_cli_ServerPasswordSet_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_ServerPasswordSet_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerPasswordSet_state ) ;
NTSTATUS status ;
NTSTATUS result ;
if ( state - > tmp_creds . negotiate_flags & NETLOGON_NEG_PASSWORD_SET2 ) {
status = dcerpc_netr_ServerPasswordSet2_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
} else {
status = dcerpc_netr_ServerPasswordSet_recv ( subreq , state ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , result ) ;
return ;
}
dcerpc_binding_handle_set_timeout ( state - > binding_handle ,
state - > old_timeout ) ;
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_ServerPasswordSet_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_ServerPasswordSet_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_ServerPasswordSet (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
2017-06-13 12:18:37 +03:00
const DATA_BLOB * new_password ,
2013-04-18 21:16:42 +04:00
const uint32_t * new_version )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_ServerPasswordSet_send ( frame , ev , context , b ,
new_password ,
new_version ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_ServerPasswordSet_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
struct netlogon_creds_cli_LogonSamLogon_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum netr_LogonInfoClass logon_level ;
const union netr_LogonLevel * const_logon ;
union netr_LogonLevel * logon ;
uint32_t flags ;
uint16_t validation_level ;
union netr_Validation * validation ;
uint8_t authoritative ;
/*
* do we need encryption at the application layer ?
*/
bool user_encrypt ;
bool try_logon_ex ;
bool try_validation6 ;
/*
* the read only credentials before we started the operation
2017-02-15 10:58:20 +03:00
* used for netr_LogonSamLogonEx ( ) if required ( validation_level = 3 ) .
2013-04-18 21:16:42 +04:00
*/
struct netlogon_creds_CredentialState * ro_creds ;
2017-02-15 10:58:20 +03:00
/*
* The ( locked ) credentials used for the credential chain
* used for netr_LogonSamLogonWithFlags ( ) or
* netr_LogonSamLogonWith ( ) .
*/
2013-04-18 21:16:42 +04:00
struct netlogon_creds_CredentialState * lk_creds ;
2017-02-15 10:58:20 +03:00
/*
* While we have locked the global credentials ( lk_creds above )
* we operate an a temporary copy , because a server
* may not support netr_LogonSamLogonWithFlags ( ) and
* didn ' t process our netr_Authenticator , so we need to
* restart from lk_creds .
*/
2013-04-18 21:16:42 +04:00
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_LogonSamLogon_start ( struct tevent_req * req ) ;
static void netlogon_creds_cli_LogonSamLogon_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
struct tevent_req * netlogon_creds_cli_LogonSamLogon_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
enum netr_LogonInfoClass logon_level ,
const union netr_LogonLevel * logon ,
uint32_t flags )
{
struct tevent_req * req ;
struct netlogon_creds_cli_LogonSamLogon_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_LogonSamLogon_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > logon_level = logon_level ;
state - > const_logon = logon ;
state - > flags = flags ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
switch ( logon_level ) {
case NetlogonInteractiveInformation :
case NetlogonInteractiveTransitiveInformation :
case NetlogonServiceInformation :
case NetlogonServiceTransitiveInformation :
case NetlogonGenericInformation :
state - > user_encrypt = true ;
break ;
case NetlogonNetworkInformation :
case NetlogonNetworkTransitiveInformation :
2024-11-21 16:16:12 +03:00
case NetlogonTicketLogonInformation :
2013-04-18 21:16:42 +04:00
break ;
}
state - > validation = talloc_zero ( state , union netr_Validation ) ;
if ( tevent_req_nomem ( state - > validation , req ) ) {
return tevent_req_post ( req , ev ) ;
}
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
return req ;
}
static void netlogon_creds_cli_LogonSamLogon_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_LogonSamLogon_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonSamLogon_state ) ;
if ( state - > lk_creds = = NULL ) {
return ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
/*
* This is a hack to recover from a bug in old
* Samba servers , when LogonSamLogonEx ( ) fails :
*
* api_net_sam_logon_ex : Failed to marshall NET_R_SAM_LOGON_EX .
*
* All following request will get NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE .
*
* A second bug generates NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ,
* instead of NT_STATUS_ACCESS_DENIED or NT_STATUS_RPC_SEC_PKG_ERROR
* If the sign / seal check fails .
*
* In that case we need to cleanup the netlogon session .
*
* It ' s the job of the caller to disconnect the current
* connection , if netlogon_creds_cli_LogonSamLogon ( )
* returns NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE .
*/
if ( ! state - > context - > server . try_logon_with ) {
status = NT_STATUS_NETWORK_ACCESS_DENIED ;
}
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > lk_creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > lk_creds ) ;
TALLOC_FREE ( state - > lk_creds ) ;
2013-04-18 21:16:42 +04:00
}
static void netlogon_creds_cli_LogonSamLogon_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_LogonSamLogon_start ( struct tevent_req * req )
{
struct netlogon_creds_cli_LogonSamLogon_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonSamLogon_state ) ;
struct tevent_req * subreq ;
NTSTATUS status ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
TALLOC_FREE ( state - > ro_creds ) ;
TALLOC_FREE ( state - > logon ) ;
ZERO_STRUCTP ( state - > validation ) ;
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& auth_type , & auth_level ) ;
state - > try_logon_ex = state - > context - > server . try_logon_ex ;
state - > try_validation6 = state - > context - > server . try_validation6 ;
if ( auth_type ! = DCERPC_AUTH_TYPE_SCHANNEL ) {
state - > try_logon_ex = false ;
}
if ( auth_level ! = DCERPC_AUTH_LEVEL_PRIVACY ) {
state - > try_validation6 = false ;
}
if ( state - > try_logon_ex ) {
if ( state - > try_validation6 ) {
state - > validation_level = 6 ;
} else {
state - > validation_level = 3 ;
state - > user_encrypt = true ;
}
state - > logon = netlogon_creds_shallow_copy_logon ( state ,
state - > logon_level ,
state - > const_logon ) ;
if ( tevent_req_nomem ( state - > logon , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
if ( state - > user_encrypt ) {
status = netlogon_creds_cli_get ( state - > context ,
state ,
& state - > ro_creds ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
status = NT_STATUS_ACCESS_DENIED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2019-05-29 17:46:36 +03:00
status = netlogon_creds_encrypt_samlogon_logon ( state - > ro_creds ,
state - > logon_level ,
2024-10-28 14:55:12 +03:00
state - > logon ,
auth_type ,
auth_level ) ;
2019-05-29 17:46:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
status = NT_STATUS_ACCESS_DENIED ;
tevent_req_nterror ( req , status ) ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2013-04-18 21:16:42 +04:00
}
subreq = dcerpc_netr_LogonSamLogonEx_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
state - > logon_level ,
state - > logon ,
state - > validation_level ,
state - > validation ,
& state - > authoritative ,
& state - > flags ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_LogonSamLogon_done ,
req ) ;
return ;
}
if ( state - > lk_creds = = NULL ) {
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_LogonSamLogon_done ,
req ) ;
return ;
}
state - > tmp_creds = * state - > lk_creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2013-04-18 21:16:42 +04:00
ZERO_STRUCT ( state - > rep_auth ) ;
state - > logon = netlogon_creds_shallow_copy_logon ( state ,
state - > logon_level ,
state - > const_logon ) ;
if ( tevent_req_nomem ( state - > logon , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2019-05-29 17:46:36 +03:00
status = netlogon_creds_encrypt_samlogon_logon ( & state - > tmp_creds ,
state - > logon_level ,
2024-10-28 14:55:12 +03:00
state - > logon ,
auth_type ,
auth_level ) ;
2019-05-29 17:46:36 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2013-04-18 21:16:42 +04:00
state - > validation_level = 3 ;
if ( state - > context - > server . try_logon_with ) {
subreq = dcerpc_netr_LogonSamLogonWithFlags_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
& state - > req_auth ,
& state - > rep_auth ,
state - > logon_level ,
state - > logon ,
state - > validation_level ,
state - > validation ,
& state - > authoritative ,
& state - > flags ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
} else {
state - > flags = 0 ;
subreq = dcerpc_netr_LogonSamLogon_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > context - > client . computer ,
& state - > req_auth ,
& state - > rep_auth ,
state - > logon_level ,
state - > logon ,
state - > validation_level ,
state - > validation ,
& state - > authoritative ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_LogonSamLogon_done ,
req ) ;
}
static void netlogon_creds_cli_LogonSamLogon_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_LogonSamLogon_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonSamLogon_state ) ;
2024-10-28 14:43:44 +03:00
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
2013-04-18 21:16:42 +04:00
NTSTATUS status ;
NTSTATUS result ;
bool ok ;
2024-10-28 14:43:44 +03:00
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& auth_type ,
& auth_level ) ;
2013-04-18 21:16:42 +04:00
if ( state - > try_logon_ex ) {
status = dcerpc_netr_LogonSamLogonEx_recv ( subreq ,
state - > validation ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
state - > context - > server . try_validation6 = false ;
state - > context - > server . try_logon_ex = false ;
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
if ( ( state - > validation_level = = 6 ) & &
( NT_STATUS_EQUAL ( result , NT_STATUS_INVALID_INFO_CLASS ) | |
NT_STATUS_EQUAL ( result , NT_STATUS_INVALID_PARAMETER ) | |
NT_STATUS_EQUAL ( result , NT_STATUS_BUFFER_TOO_SMALL ) ) )
{
state - > context - > server . try_validation6 = false ;
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , result ) ;
return ;
}
if ( state - > ro_creds = = NULL ) {
tevent_req_done ( req ) ;
return ;
}
ok = netlogon_creds_cli_validate ( state - > context , state - > ro_creds ) ;
if ( ! ok ) {
/*
* We got a race , lets retry with on authenticator
* protection .
2017-02-15 10:58:20 +03:00
*
* netlogon_creds_cli_LogonSamLogon_start ( )
* will TALLOC_FREE ( state - > ro_creds ) ;
2013-04-18 21:16:42 +04:00
*/
state - > try_logon_ex = false ;
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
return ;
}
2019-05-29 15:35:20 +03:00
status = netlogon_creds_decrypt_samlogon_validation ( state - > ro_creds ,
state - > validation_level ,
2024-10-28 14:43:44 +03:00
state - > validation ,
auth_type ,
auth_level ) ;
2019-05-29 15:35:20 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
2013-04-18 21:16:42 +04:00
tevent_req_done ( req ) ;
return ;
}
if ( state - > lk_creds = = NULL ) {
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > lk_creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
return ;
}
if ( state - > context - > server . try_logon_with ) {
status = dcerpc_netr_LogonSamLogonWithFlags_recv ( subreq ,
state - > validation ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE ) ) {
state - > context - > server . try_logon_with = false ;
netlogon_creds_cli_LogonSamLogon_start ( req ) ;
return ;
}
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
} else {
status = dcerpc_netr_LogonSamLogon_recv ( subreq ,
state - > validation ,
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
auth_type ,
auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2013-04-18 21:16:42 +04:00
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
* state - > lk_creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > lk_creds ) ;
TALLOC_FREE ( state - > lk_creds ) ;
2013-04-18 21:16:42 +04:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , result ) ;
return ;
}
2019-05-29 15:35:20 +03:00
status = netlogon_creds_decrypt_samlogon_validation ( & state - > tmp_creds ,
state - > validation_level ,
2024-10-28 14:43:44 +03:00
state - > validation ,
auth_type ,
auth_level ) ;
2019-08-14 16:31:07 +03:00
if ( tevent_req_nterror ( req , status ) ) {
2019-05-29 15:35:20 +03:00
netlogon_creds_cli_LogonSamLogon_cleanup ( req , result ) ;
return ;
}
2013-04-18 21:16:42 +04:00
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_LogonSamLogon_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
uint16_t * validation_level ,
union netr_Validation * * validation ,
uint8_t * authoritative ,
uint32_t * flags )
{
struct netlogon_creds_cli_LogonSamLogon_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonSamLogon_state ) ;
NTSTATUS status ;
/* authoritative is also returned on error */
* authoritative = state - > authoritative ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_LogonSamLogon_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
* validation_level = state - > validation_level ;
* validation = talloc_move ( mem_ctx , & state - > validation ) ;
* flags = state - > flags ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_LogonSamLogon (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
enum netr_LogonInfoClass logon_level ,
const union netr_LogonLevel * logon ,
TALLOC_CTX * mem_ctx ,
uint16_t * validation_level ,
union netr_Validation * * validation ,
uint8_t * authoritative ,
uint32_t * flags )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_LogonSamLogon_send ( frame , ev , context , b ,
logon_level , logon ,
* flags ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_LogonSamLogon_recv ( req , mem_ctx ,
validation_level ,
validation ,
authoritative ,
flags ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2014-05-06 09:00:09 +04:00
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
const char * site_name ;
uint32_t dns_ttl ;
struct NL_DNS_NAME_INFO_ARRAY * dns_names ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
const char * site_name ,
uint32_t dns_ttl ,
struct NL_DNS_NAME_INFO_ARRAY * dns_names )
{
struct tevent_req * req ;
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > site_name = site_name ;
state - > dns_ttl = dns_ttl ;
state - > dns_names = dns_names ;
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2014-05-06 09:00:09 +04:00
}
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-10-29 15:42:06 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_NONE ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2014-05-06 09:00:09 +04:00
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2014-05-06 09:00:09 +04:00
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
state - > site_name ,
state - > dns_ttl ,
state - > dns_names ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done ,
req ) ;
}
static void netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_state ) ;
NTSTATUS status ;
NTSTATUS result ;
2014-07-24 07:54:58 +04:00
/*
* We use state - > dns_names as the memory context , as this is
* the only in / out variable and it has been overwritten by the
* out parameter from the server .
*
* We need to preserve the return value until the caller can use it .
*/
status = dcerpc_netr_DsrUpdateReadOnlyServerDnsRecords_recv ( subreq , state - > dns_names ,
2014-05-06 09:00:09 +04:00
& result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-05-06 09:00:09 +04:00
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , status ) ;
return ;
}
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2017-04-20 07:55:58 +03:00
2014-05-06 09:00:09 +04:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , status ) ;
return ;
}
2017-04-20 07:55:58 +03:00
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , result ) ;
return ;
}
2014-05-06 09:00:09 +04:00
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
const char * site_name ,
uint32_t dns_ttl ,
struct NL_DNS_NAME_INFO_ARRAY * dns_names )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_send ( frame , ev , context , b ,
site_name ,
dns_ttl ,
dns_names ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_DsrUpdateReadOnlyServerDnsRecords_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2014-12-22 23:48:18 +03:00
struct netlogon_creds_cli_ServerGetTrustInfo_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
struct samr_Password new_owf_password ;
struct samr_Password old_owf_password ;
struct netr_TrustInfo * trust_info ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_ServerGetTrustInfo_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_ServerGetTrustInfo_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_ServerGetTrustInfo_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b )
{
struct tevent_req * req ;
struct netlogon_creds_cli_ServerGetTrustInfo_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_ServerGetTrustInfo_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_ServerGetTrustInfo_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_ServerGetTrustInfo_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_ServerGetTrustInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerGetTrustInfo_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2014-12-22 23:48:18 +03:00
}
static void netlogon_creds_cli_ServerGetTrustInfo_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_ServerGetTrustInfo_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_ServerGetTrustInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerGetTrustInfo_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-10-29 15:42:06 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_PRIVACY ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-12-22 23:48:18 +03:00
return ;
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2014-12-22 23:48:18 +03:00
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_ServerGetTrustInfo_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . account_name ,
state - > tmp_creds . secure_channel_type ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
& state - > new_owf_password ,
& state - > old_owf_password ,
& state - > trust_info ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_ServerGetTrustInfo_done ,
req ) ;
}
static void netlogon_creds_cli_ServerGetTrustInfo_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_ServerGetTrustInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerGetTrustInfo_state ) ;
NTSTATUS status ;
NTSTATUS result ;
/*
* We use state - > dns_names as the memory context , as this is
* the only in / out variable and it has been overwritten by the
* out parameter from the server .
*
* We need to preserve the return value until the caller can use it .
*/
status = dcerpc_netr_ServerGetTrustInfo_recv ( subreq , state , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-12-22 23:48:18 +03:00
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
}
2024-10-28 19:19:09 +03:00
status = netlogon_creds_decrypt_samr_Password ( & state - > tmp_creds ,
& state - > new_owf_password ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
2014-12-22 23:48:18 +03:00
}
2024-10-28 19:19:09 +03:00
status = netlogon_creds_decrypt_samr_Password ( & state - > tmp_creds ,
& state - > old_owf_password ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
2014-12-22 23:48:18 +03:00
}
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2014-12-22 23:48:18 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
return ;
}
2017-04-20 07:55:58 +03:00
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , result ) ;
return ;
}
2014-12-22 23:48:18 +03:00
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_ServerGetTrustInfo_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct samr_Password * new_owf_password ,
struct samr_Password * old_owf_password ,
struct netr_TrustInfo * * trust_info )
{
struct netlogon_creds_cli_ServerGetTrustInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_ServerGetTrustInfo_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_ServerGetTrustInfo_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
if ( new_owf_password ! = NULL ) {
* new_owf_password = state - > new_owf_password ;
}
if ( old_owf_password ! = NULL ) {
* old_owf_password = state - > old_owf_password ;
}
if ( trust_info ! = NULL ) {
* trust_info = talloc_move ( mem_ctx , & state - > trust_info ) ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_ServerGetTrustInfo (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
TALLOC_CTX * mem_ctx ,
struct samr_Password * new_owf_password ,
struct samr_Password * old_owf_password ,
struct netr_TrustInfo * * trust_info )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_ServerGetTrustInfo_send ( frame , ev , context , b ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_ServerGetTrustInfo_recv ( req ,
mem_ctx ,
new_owf_password ,
old_owf_password ,
trust_info ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2014-12-23 00:02:04 +03:00
struct netlogon_creds_cli_GetForestTrustInformation_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
uint32_t flags ;
struct lsa_ForestTrustInformation * forest_trust_info ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_GetForestTrustInformation_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_GetForestTrustInformation_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_GetForestTrustInformation_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b )
{
struct tevent_req * req ;
struct netlogon_creds_cli_GetForestTrustInformation_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_GetForestTrustInformation_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > flags = 0 ;
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_GetForestTrustInformation_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_GetForestTrustInformation_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_GetForestTrustInformation_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_GetForestTrustInformation_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2014-12-23 00:02:04 +03:00
}
static void netlogon_creds_cli_GetForestTrustInformation_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_GetForestTrustInformation_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_GetForestTrustInformation_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_GetForestTrustInformation_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-11-06 19:18:58 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_NONE ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2014-12-23 00:02:04 +03:00
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2014-12-23 00:02:04 +03:00
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_GetForestTrustInformation_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
state - > flags ,
& state - > forest_trust_info ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_GetForestTrustInformation_done ,
req ) ;
}
static void netlogon_creds_cli_GetForestTrustInformation_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_GetForestTrustInformation_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_GetForestTrustInformation_state ) ;
NTSTATUS status ;
NTSTATUS result ;
/*
* We use state - > dns_names as the memory context , as this is
* the only in / out variable and it has been overwritten by the
* out parameter from the server .
*
* We need to preserve the return value until the caller can use it .
*/
status = dcerpc_netr_GetForestTrustInformation_recv ( subreq , state , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2014-12-23 00:02:04 +03:00
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , status ) ;
return ;
}
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2017-04-20 07:55:58 +03:00
2014-12-23 00:02:04 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , status ) ;
return ;
}
2017-04-20 07:55:58 +03:00
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , result ) ;
return ;
}
2014-12-23 00:02:04 +03:00
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_GetForestTrustInformation_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct lsa_ForestTrustInformation * * forest_trust_info )
{
struct netlogon_creds_cli_GetForestTrustInformation_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_GetForestTrustInformation_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_GetForestTrustInformation_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
* forest_trust_info = talloc_move ( mem_ctx , & state - > forest_trust_info ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_GetForestTrustInformation (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
TALLOC_CTX * mem_ctx ,
struct lsa_ForestTrustInformation * * forest_trust_info )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_GetForestTrustInformation_send ( frame , ev , context , b ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_GetForestTrustInformation_recv ( req ,
mem_ctx ,
forest_trust_info ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2017-04-11 06:51:50 +03:00
struct netlogon_creds_cli_SendToSam_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
DATA_BLOB opaque ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_SendToSam_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_SendToSam_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_SendToSam_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
struct netr_SendToSamBase * message )
{
struct tevent_req * req ;
struct netlogon_creds_cli_SendToSam_state * state ;
struct tevent_req * subreq ;
enum ndr_err_code ndr_err ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_SendToSam_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
ndr_err = ndr_push_struct_blob ( & state - > opaque , mem_ctx , message ,
( ndr_push_flags_fn_t ) ndr_push_netr_SendToSamBase ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
NTSTATUS status = ndr_map_error2ntstatus ( ndr_err ) ;
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
}
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_SendToSam_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_SendToSam_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_SendToSam_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_SendToSam_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
2017-09-10 15:55:13 +03:00
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2017-04-11 06:51:50 +03:00
}
static void netlogon_creds_cli_SendToSam_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_SendToSam_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_SendToSam_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_SendToSam_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-11-06 19:18:58 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_NONE ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2017-04-11 06:51:50 +03:00
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2017-04-11 06:51:50 +03:00
ZERO_STRUCT ( state - > rep_auth ) ;
2024-10-28 18:00:52 +03:00
status = netlogon_creds_encrypt_SendToSam ( & state - > tmp_creds ,
state - > opaque . data ,
state - > opaque . length ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
return ;
2017-04-11 06:51:50 +03:00
}
subreq = dcerpc_netr_NetrLogonSendToSam_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
state - > opaque . data ,
state - > opaque . length ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_SendToSam_done ,
req ) ;
}
static void netlogon_creds_cli_SendToSam_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_SendToSam_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_SendToSam_state ) ;
NTSTATUS status ;
NTSTATUS result ;
status = dcerpc_netr_NetrLogonSendToSam_recv ( subreq , state , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2017-04-11 06:51:50 +03:00
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
return ;
}
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
2017-09-10 15:55:13 +03:00
state - > creds ) ;
TALLOC_FREE ( state - > creds ) ;
2017-04-11 06:51:50 +03:00
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
return ;
}
/*
* Creds must be stored before we send back application errors
* e . g . NT_STATUS_NOT_IMPLEMENTED
*/
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_SendToSam_cleanup ( req , result ) ;
return ;
}
tevent_req_done ( req ) ;
}
2018-04-16 17:08:29 +03:00
NTSTATUS netlogon_creds_cli_SendToSam_recv ( struct tevent_req * req )
{
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_SendToSam_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2017-04-11 06:51:50 +03:00
NTSTATUS netlogon_creds_cli_SendToSam ( struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
struct netr_SendToSamBase * message )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
2018-04-16 17:08:29 +03:00
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2017-04-11 06:51:50 +03:00
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_SendToSam_send ( frame , ev , context , b , message ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
2018-04-16 17:08:29 +03:00
status = netlogon_creds_cli_SendToSam_recv ( req ) ;
2017-04-11 06:51:50 +03:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
}
2015-07-20 15:00:05 +03:00
struct netlogon_creds_cli_LogonGetDomainInfo_state {
struct tevent_context * ev ;
struct netlogon_creds_cli_context * context ;
struct dcerpc_binding_handle * binding_handle ;
char * srv_name_slash ;
enum dcerpc_AuthType auth_type ;
enum dcerpc_AuthLevel auth_level ;
uint32_t level ;
union netr_WorkstationInfo * query ;
union netr_DomainInfo * info ;
struct netlogon_creds_CredentialState * creds ;
struct netlogon_creds_CredentialState tmp_creds ;
struct netr_Authenticator req_auth ;
struct netr_Authenticator rep_auth ;
} ;
static void netlogon_creds_cli_LogonGetDomainInfo_cleanup ( struct tevent_req * req ,
NTSTATUS status ) ;
static void netlogon_creds_cli_LogonGetDomainInfo_locked ( struct tevent_req * subreq ) ;
struct tevent_req * netlogon_creds_cli_LogonGetDomainInfo_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
uint32_t level ,
union netr_WorkstationInfo * query )
{
struct tevent_req * req ;
struct netlogon_creds_cli_LogonGetDomainInfo_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct netlogon_creds_cli_LogonGetDomainInfo_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > context = context ;
state - > binding_handle = b ;
state - > srv_name_slash = talloc_asprintf ( state , " \\ \\ %s " ,
context - > server . computer ) ;
if ( tevent_req_nomem ( state - > srv_name_slash , req ) ) {
return tevent_req_post ( req , ev ) ;
}
state - > level = level ;
state - > query = query ;
state - > info = talloc_zero ( state , union netr_DomainInfo ) ;
if ( tevent_req_nomem ( state - > info , req ) ) {
return tevent_req_post ( req , ev ) ;
}
dcerpc_binding_handle_auth_info ( state - > binding_handle ,
& state - > auth_type ,
& state - > auth_level ) ;
subreq = netlogon_creds_cli_lock_send ( state , state - > ev ,
state - > context ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_LogonGetDomainInfo_locked ,
req ) ;
return req ;
}
static void netlogon_creds_cli_LogonGetDomainInfo_cleanup ( struct tevent_req * req ,
NTSTATUS status )
{
struct netlogon_creds_cli_LogonGetDomainInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonGetDomainInfo_state ) ;
if ( state - > creds = = NULL ) {
return ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_DOWNGRADE_DETECTED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_RPC_SEC_PKG_ERROR ) ) {
TALLOC_FREE ( state - > creds ) ;
return ;
}
netlogon_creds_cli_delete ( state - > context , state - > creds ) ;
}
static void netlogon_creds_cli_LogonGetDomainInfo_done ( struct tevent_req * subreq ) ;
static void netlogon_creds_cli_LogonGetDomainInfo_locked ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_LogonGetDomainInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonGetDomainInfo_state ) ;
NTSTATUS status ;
status = netlogon_creds_cli_lock_recv ( subreq , state ,
& state - > creds ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2024-11-06 19:18:58 +03:00
status = netlogon_creds_cli_check_transport ( state - > auth_type ,
state - > auth_level ,
state - > creds ,
DCERPC_AUTH_LEVEL_NONE ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
2015-07-20 15:00:05 +03:00
}
/*
* we defer all callbacks in order to cleanup
* the database record .
*/
tevent_req_defer_callback ( req , state - > ev ) ;
state - > tmp_creds = * state - > creds ;
2019-11-13 12:06:20 +03:00
status = netlogon_creds_client_authenticator ( & state - > tmp_creds ,
& state - > req_auth ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2015-07-20 15:00:05 +03:00
ZERO_STRUCT ( state - > rep_auth ) ;
subreq = dcerpc_netr_LogonGetDomainInfo_send ( state , state - > ev ,
state - > binding_handle ,
state - > srv_name_slash ,
state - > tmp_creds . computer_name ,
& state - > req_auth ,
& state - > rep_auth ,
state - > level ,
state - > query ,
state - > info ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
status = NT_STATUS_NO_MEMORY ;
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , status ) ;
return ;
}
tevent_req_set_callback ( subreq ,
netlogon_creds_cli_LogonGetDomainInfo_done ,
req ) ;
}
static void netlogon_creds_cli_LogonGetDomainInfo_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct netlogon_creds_cli_LogonGetDomainInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonGetDomainInfo_state ) ;
NTSTATUS status ;
NTSTATUS result ;
/*
* We use state - > dns_names as the memory context , as this is
* the only in / out variable and it has been overwritten by the
* out parameter from the server .
*
* We need to preserve the return value until the caller can use it .
*/
status = dcerpc_netr_LogonGetDomainInfo_recv ( subreq , state - > info , & result ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , status ) ;
return ;
}
2024-10-29 12:02:40 +03:00
status = netlogon_creds_client_verify ( & state - > tmp_creds ,
& state - > rep_auth . cred ,
state - > auth_type ,
state - > auth_level ) ;
if ( tevent_req_nterror ( req , status ) ) {
2015-07-20 15:00:05 +03:00
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , status ) ;
return ;
}
if ( tevent_req_nterror ( req , result ) ) {
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , result ) ;
return ;
}
* state - > creds = state - > tmp_creds ;
status = netlogon_creds_cli_store ( state - > context ,
state - > creds ) ;
if ( tevent_req_nterror ( req , status ) ) {
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
NTSTATUS netlogon_creds_cli_LogonGetDomainInfo_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
union netr_DomainInfo * * info )
{
struct netlogon_creds_cli_LogonGetDomainInfo_state * state =
tevent_req_data ( req ,
struct netlogon_creds_cli_LogonGetDomainInfo_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
netlogon_creds_cli_LogonGetDomainInfo_cleanup ( req , status ) ;
tevent_req_received ( req ) ;
return status ;
}
* info = talloc_move ( mem_ctx , & state - > info ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
NTSTATUS netlogon_creds_cli_LogonGetDomainInfo (
struct netlogon_creds_cli_context * context ,
struct dcerpc_binding_handle * b ,
TALLOC_CTX * mem_ctx ,
uint32_t level ,
union netr_WorkstationInfo * query ,
union netr_DomainInfo * * info )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_OK ;
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = netlogon_creds_cli_LogonGetDomainInfo_send ( frame , ev , context , b ,
level , query ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = netlogon_creds_cli_LogonGetDomainInfo_recv ( req ,
mem_ctx ,
info ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
}