2005-05-02 18:17:19 +04:00
/*
Unix SMB / CIFS implementation .
2005-05-21 10:31:02 +04:00
Extract the user / system database from a remote SamSync server
2005-05-02 18:17:19 +04:00
Copyright ( C ) Andrew Bartlett < abartlet @ samba . org > 2004 - 2005
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2005-05-02 18:17:19 +04:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2005-05-02 18:17:19 +04:00
*/
# include "includes.h"
# include "libnet/libnet.h"
2006-03-14 18:03:25 +03:00
# include "libcli/auth/libcli_auth.h"
2006-03-18 18:42:57 +03:00
# include "auth/gensec/gensec.h"
2006-11-07 03:48:36 +03:00
# include "auth/credentials/credentials.h"
2006-03-07 02:28:18 +03:00
# include "auth/gensec/schannel_proto.h"
2006-03-16 03:23:11 +03:00
# include "librpc/gen_ndr/ndr_netlogon.h"
2006-03-15 02:35:30 +03:00
# include "librpc/gen_ndr/ndr_netlogon_c.h"
2005-05-21 09:05:44 +04:00
/**
* Decrypt and extract the user ' s passwords .
*
* The writes decrypted ( no longer ' RID encrypted ' or arcfour encrypted ) passwords back into the structure
*/
static NTSTATUS fix_user ( TALLOC_CTX * mem_ctx ,
struct creds_CredentialState * creds ,
2007-02-12 16:34:04 +03:00
bool rid_crypt ,
2005-05-21 09:05:44 +04:00
enum netr_SamDatabaseID database ,
struct netr_DELTA_ENUM * delta ,
char * * error_string )
{
uint32_t rid = delta - > delta_id_union . rid ;
struct netr_DELTA_USER * user = delta - > delta_union . user ;
struct samr_Password lm_hash ;
struct samr_Password nt_hash ;
const char * username = user - > account_name . string ;
2005-05-02 18:17:19 +04:00
NTSTATUS nt_status ;
2007-02-12 16:34:04 +03:00
if ( rid_crypt ) {
2007-02-09 04:49:26 +03:00
if ( user - > lm_password_present ) {
sam_rid_crypt ( rid , user - > lmpassword . hash , lm_hash . hash , 0 ) ;
user - > lmpassword = lm_hash ;
}
if ( user - > nt_password_present ) {
sam_rid_crypt ( rid , user - > ntpassword . hash , nt_hash . hash , 0 ) ;
user - > ntpassword = nt_hash ;
}
2005-05-02 18:17:19 +04:00
}
if ( user - > user_private_info . SensitiveData ) {
DATA_BLOB data ;
struct netr_USER_KEYS keys ;
data . data = user - > user_private_info . SensitiveData ;
data . length = user - > user_private_info . DataLength ;
creds_arcfour_crypt ( creds , data . data , data . length ) ;
2005-05-21 09:05:44 +04:00
user - > user_private_info . SensitiveData = data . data ;
user - > user_private_info . DataLength = data . length ;
2005-05-02 18:17:19 +04:00
nt_status = ndr_pull_struct_blob ( & data , mem_ctx , & keys , ( ndr_pull_flags_fn_t ) ndr_pull_netr_USER_KEYS ) ;
if ( NT_STATUS_IS_OK ( nt_status ) ) {
if ( keys . keys . keys2 . lmpassword . length = = 16 ) {
2007-02-12 16:34:04 +03:00
if ( rid_crypt ) {
2007-02-09 04:49:26 +03:00
sam_rid_crypt ( rid , keys . keys . keys2 . lmpassword . pwd . hash , lm_hash . hash , 0 ) ;
user - > lmpassword = lm_hash ;
} else {
user - > lmpassword = keys . keys . keys2 . lmpassword . pwd ;
}
2005-05-21 09:05:44 +04:00
user - > lm_password_present = True ;
2005-05-02 18:17:19 +04:00
}
if ( keys . keys . keys2 . ntpassword . length = = 16 ) {
2007-02-12 16:34:04 +03:00
if ( rid_crypt ) {
2007-02-09 04:49:26 +03:00
sam_rid_crypt ( rid , keys . keys . keys2 . ntpassword . pwd . hash , nt_hash . hash , 0 ) ;
user - > ntpassword = nt_hash ;
} else {
user - > ntpassword = keys . keys . keys2 . ntpassword . pwd ;
}
2005-05-21 09:05:44 +04:00
user - > nt_password_present = True ;
2005-05-02 18:17:19 +04:00
}
2007-02-12 16:34:04 +03:00
/* TODO: rid decrypt history fields */
2005-05-02 18:17:19 +04:00
} else {
2006-09-18 13:54:44 +04:00
* error_string = talloc_asprintf ( mem_ctx , " Failed to parse Sensitive Data for %s: " , username ) ;
2005-05-02 18:17:19 +04:00
dump_data ( 10 , data . data , data . length ) ;
2005-05-03 18:38:14 +04:00
return nt_status ;
2005-05-02 18:17:19 +04:00
}
}
2005-05-03 18:38:14 +04:00
return NT_STATUS_OK ;
2005-05-02 18:17:19 +04:00
}
2005-05-21 10:31:02 +04:00
/**
* Decrypt and extract the secrets
*
* The writes decrypted secrets back into the structure
*/
static NTSTATUS fix_secret ( TALLOC_CTX * mem_ctx ,
struct creds_CredentialState * creds ,
enum netr_SamDatabaseID database ,
struct netr_DELTA_ENUM * delta ,
char * * error_string )
{
struct netr_DELTA_SECRET * secret = delta - > delta_union . secret ;
creds_arcfour_crypt ( creds , secret - > current_cipher . cipher_data ,
secret - > current_cipher . maxlen ) ;
creds_arcfour_crypt ( creds , secret - > old_cipher . cipher_data ,
secret - > old_cipher . maxlen ) ;
return NT_STATUS_OK ;
}
2005-05-21 09:05:44 +04:00
/**
* Fix up the delta , dealing with encryption issues so that the final
* callback need only do the printing or application logic
*/
static NTSTATUS fix_delta ( TALLOC_CTX * mem_ctx ,
struct creds_CredentialState * creds ,
2007-02-12 16:34:04 +03:00
bool rid_crypt ,
2005-05-21 09:05:44 +04:00
enum netr_SamDatabaseID database ,
struct netr_DELTA_ENUM * delta ,
char * * error_string )
2005-05-03 18:38:14 +04:00
{
NTSTATUS nt_status = NT_STATUS_OK ;
* error_string = NULL ;
2005-05-21 09:05:44 +04:00
switch ( delta - > delta_type ) {
case NETR_DELTA_USER :
2005-05-03 18:38:14 +04:00
{
2005-05-21 09:05:44 +04:00
nt_status = fix_user ( mem_ctx ,
creds ,
2007-02-12 16:34:04 +03:00
rid_crypt ,
2005-05-21 09:05:44 +04:00
database ,
delta ,
error_string ) ;
2005-05-03 18:38:14 +04:00
break ;
}
2005-05-21 10:31:02 +04:00
case NETR_DELTA_SECRET :
{
nt_status = fix_secret ( mem_ctx ,
creds ,
database ,
delta ,
error_string ) ;
break ;
}
2005-07-25 05:19:16 +04:00
default :
break ;
2005-05-03 18:38:14 +04:00
}
return nt_status ;
}
2005-05-02 18:17:19 +04:00
2005-07-25 06:23:41 +04:00
NTSTATUS libnet_SamSync_netlogon ( struct libnet_context * ctx , TALLOC_CTX * mem_ctx , struct libnet_SamSync * r )
2005-05-02 18:17:19 +04:00
{
2005-05-03 18:38:14 +04:00
NTSTATUS nt_status , dbsync_nt_status ;
2005-07-25 06:23:41 +04:00
TALLOC_CTX * samsync_ctx , * loop_ctx , * delta_ctx ;
2005-05-02 18:17:19 +04:00
struct creds_CredentialState * creds ;
struct netr_DatabaseSync dbsync ;
struct cli_credentials * machine_account ;
struct dcerpc_pipe * p ;
2006-01-12 12:33:49 +03:00
struct libnet_context * machine_net_ctx ;
2006-05-04 18:52:03 +04:00
struct libnet_RpcConnect * c ;
2006-01-14 04:29:38 +03:00
struct libnet_SamSync_state * state ;
2005-05-03 18:38:14 +04:00
const enum netr_SamDatabaseID database_ids [ ] = { SAM_DATABASE_DOMAIN , SAM_DATABASE_BUILTIN , SAM_DATABASE_PRIVS } ;
int i ;
2005-05-02 18:17:19 +04:00
2005-07-25 06:23:41 +04:00
samsync_ctx = talloc_named ( mem_ctx , 0 , " SamSync top context " ) ;
2005-05-02 18:17:19 +04:00
2006-01-12 12:33:49 +03:00
if ( ! r - > in . machine_account ) {
2005-07-25 06:23:41 +04:00
machine_account = cli_credentials_init ( samsync_ctx ) ;
if ( ! machine_account ) {
talloc_free ( samsync_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
cli_credentials_set_conf ( machine_account ) ;
nt_status = cli_credentials_set_machine_account ( machine_account ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-12 12:33:49 +03:00
r - > out . error_string = talloc_strdup ( mem_ctx , " Could not obtain machine account password - are we joined to the domain? " ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
return nt_status ;
}
} else {
2006-01-12 12:33:49 +03:00
machine_account = r - > in . machine_account ;
2005-05-02 18:17:19 +04:00
}
2005-07-25 06:23:41 +04:00
2006-01-12 12:33:49 +03:00
/* We cannot do this unless we are a BDC. Check, before we get odd errors later */
2005-05-02 18:17:19 +04:00
if ( cli_credentials_get_secure_channel_type ( machine_account ) ! = SEC_CHAN_BDC ) {
2006-01-12 12:33:49 +03:00
r - > out . error_string
2005-05-02 18:17:19 +04:00
= talloc_asprintf ( mem_ctx ,
" Our join to domain %s is not as a BDC (%d), please rejoin as a BDC " ,
2005-05-03 18:38:14 +04:00
cli_credentials_get_domain ( machine_account ) ,
cli_credentials_get_secure_channel_type ( machine_account ) ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-02 18:17:19 +04:00
return NT_STATUS_CANT_ACCESS_DOMAIN_INFO ;
}
2006-05-04 18:52:03 +04:00
c = talloc ( samsync_ctx , struct libnet_RpcConnect ) ;
2006-01-12 12:33:49 +03:00
if ( ! c ) {
r - > out . error_string = NULL ;
talloc_free ( samsync_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
2006-05-04 18:52:03 +04:00
c - > level = LIBNET_RPC_CONNECT_DC_INFO ;
2006-01-12 12:33:49 +03:00
if ( r - > in . binding_string ) {
c - > in . binding = r - > in . binding_string ;
2006-10-13 11:25:51 +04:00
c - > in . name = NULL ;
2006-01-12 12:33:49 +03:00
} else {
2006-10-13 11:25:51 +04:00
c - > in . binding = NULL ;
2006-01-12 12:33:49 +03:00
c - > in . name = cli_credentials_get_domain ( machine_account ) ;
}
2006-05-04 18:52:03 +04:00
/* prepare connect to the NETLOGON pipe of PDC */
2007-08-20 01:23:03 +04:00
c - > in . dcerpc_iface = & ndr_table_netlogon ;
2006-01-12 12:33:49 +03:00
/* We must do this as the machine, not as any command-line
* user . So we override the credentials in the
* libnet_context */
machine_net_ctx = talloc ( samsync_ctx , struct libnet_context ) ;
if ( ! machine_net_ctx ) {
r - > out . error_string = NULL ;
talloc_free ( samsync_ctx ) ;
return NT_STATUS_NO_MEMORY ;
}
* machine_net_ctx = * ctx ;
machine_net_ctx - > cred = machine_account ;
2005-05-02 18:17:19 +04:00
2006-01-12 12:33:49 +03:00
/* connect to the NETLOGON pipe of the PDC */
2006-05-04 18:52:03 +04:00
nt_status = libnet_RpcConnect ( machine_net_ctx , samsync_ctx , c ) ;
2005-05-02 18:17:19 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-13 00:42:26 +03:00
if ( r - > in . binding_string ) {
r - > out . error_string = talloc_asprintf ( mem_ctx ,
" Connection to NETLOGON pipe of DC %s failed: %s " ,
r - > in . binding_string , c - > out . error_string ) ;
} else {
r - > out . error_string = talloc_asprintf ( mem_ctx ,
" Connection to NETLOGON pipe of DC for %s failed: %s " ,
c - > in . name , c - > out . error_string ) ;
}
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2006-01-12 12:33:49 +03:00
return nt_status ;
2005-05-02 18:17:19 +04:00
}
2006-01-12 12:33:49 +03:00
/* This makes a new pipe, on which we can do schannel. We
* should do this in the RpcConnect code , but the abstaction
* layers do not suit yet */
nt_status = dcerpc_secondary_connection ( c - > out . dcerpc_pipe , & p ,
c - > out . dcerpc_pipe - > binding ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
r - > out . error_string = talloc_asprintf ( mem_ctx ,
" Secondary connection to NETLOGON pipe of DC %s failed: %s " ,
dcerpc_server_name ( p ) , nt_errstr ( nt_status ) ) ;
talloc_free ( samsync_ctx ) ;
return nt_status ;
}
2005-05-02 18:17:19 +04:00
2007-08-20 01:23:03 +04:00
nt_status = dcerpc_bind_auth_schannel ( samsync_ctx , p , & ndr_table_netlogon ,
2006-01-12 12:33:49 +03:00
machine_account , DCERPC_AUTH_LEVEL_PRIVACY ) ;
2005-05-02 18:17:19 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-12 12:33:49 +03:00
r - > out . error_string = talloc_asprintf ( mem_ctx ,
" SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s " ,
dcerpc_server_name ( p ) , nt_errstr ( nt_status ) ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-02 18:17:19 +04:00
return nt_status ;
}
2006-01-14 04:29:38 +03:00
state = talloc ( samsync_ctx , struct libnet_SamSync_state ) ;
if ( ! state ) {
r - > out . error_string = NULL ;
talloc_free ( samsync_ctx ) ;
return nt_status ;
}
state - > domain_name = c - > out . domain_name ;
state - > domain_sid = c - > out . domain_sid ;
state - > realm = c - > out . realm ;
state - > domain_guid = c - > out . guid ;
state - > machine_net_ctx = machine_net_ctx ;
state - > netlogon_pipe = p ;
2006-01-13 15:52:56 +03:00
/* initialise the callback layer. It may wish to contact the
* server with ldap , now we know the name */
if ( r - > in . init_fn ) {
char * error_string ;
nt_status = r - > in . init_fn ( samsync_ctx ,
r - > in . fn_ctx ,
2006-01-14 04:29:38 +03:00
state ,
2006-01-13 15:52:56 +03:00
& error_string ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
r - > out . error_string = talloc_steal ( mem_ctx , error_string ) ;
talloc_free ( samsync_ctx ) ;
return nt_status ;
}
}
2005-05-03 18:38:14 +04:00
/* get NETLOGON credentails */
2005-05-02 18:17:19 +04:00
2005-07-25 06:23:41 +04:00
nt_status = dcerpc_schannel_creds ( p - > conn - > security_state . generic_state , samsync_ctx , & creds ) ;
2005-05-02 18:17:19 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-12 12:33:49 +03:00
r - > out . error_string = talloc_strdup ( mem_ctx , " Could not obtain NETLOGON credentials from DCERPC/GENSEC layer " ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-02 18:17:19 +04:00
return nt_status ;
}
2006-05-07 17:39:53 +04:00
/* Setup details for the synchronisation */
2005-07-25 06:23:41 +04:00
dbsync . in . logon_server = talloc_asprintf ( samsync_ctx , " \\ \\ %s " , dcerpc_server_name ( p ) ) ;
2005-05-02 18:17:19 +04:00
dbsync . in . computername = cli_credentials_get_workstation ( machine_account ) ;
dbsync . in . preferredmaximumlength = ( uint32_t ) - 1 ;
ZERO_STRUCT ( dbsync . in . return_authenticator ) ;
2005-05-03 18:38:14 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( database_ids ) ; i + + ) {
dbsync . in . sync_context = 0 ;
dbsync . in . database_id = database_ids [ i ] ;
2005-05-02 18:17:19 +04:00
2005-05-03 18:38:14 +04:00
do {
int d ;
2005-07-25 06:23:41 +04:00
loop_ctx = talloc_named ( samsync_ctx , 0 , " DatabaseSync loop context " ) ;
2005-05-03 18:38:14 +04:00
creds_client_authenticator ( creds , & dbsync . in . credential ) ;
dbsync_nt_status = dcerpc_netr_DatabaseSync ( p , loop_ctx , & dbsync ) ;
if ( ! NT_STATUS_IS_OK ( dbsync_nt_status ) & &
! NT_STATUS_EQUAL ( dbsync_nt_status , STATUS_MORE_ENTRIES ) ) {
2006-01-13 15:52:56 +03:00
r - > out . error_string = talloc_asprintf ( mem_ctx , " DatabaseSync failed - %s " , nt_errstr ( nt_status ) ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-03 18:38:14 +04:00
return nt_status ;
}
if ( ! creds_client_check ( creds , & dbsync . out . return_authenticator . cred ) ) {
2006-01-13 15:52:56 +03:00
r - > out . error_string = talloc_strdup ( mem_ctx , " Credential chaining on incoming DatabaseSync failed " ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-03 18:38:14 +04:00
return NT_STATUS_ACCESS_DENIED ;
}
dbsync . in . sync_context = dbsync . out . sync_context ;
2006-01-12 12:33:49 +03:00
/* For every single remote 'delta' entry: */
2005-05-03 18:38:14 +04:00
for ( d = 0 ; d < dbsync . out . delta_enum_array - > num_deltas ; d + + ) {
char * error_string = NULL ;
delta_ctx = talloc_named ( loop_ctx , 0 , " DatabaseSync delta context " ) ;
2006-01-12 12:33:49 +03:00
/* 'Fix' elements, by decrypting and
2006-05-07 17:39:53 +04:00
* de - obfuscating the data */
2005-05-21 09:05:44 +04:00
nt_status = fix_delta ( delta_ctx ,
creds ,
2007-02-12 16:34:04 +03:00
r - > in . rid_crypt ,
2005-05-21 09:05:44 +04:00
dbsync . in . database_id ,
& dbsync . out . delta_enum_array - > delta_enum [ d ] ,
& error_string ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-13 15:52:56 +03:00
r - > out . error_string = talloc_steal ( mem_ctx , error_string ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-21 09:05:44 +04:00
return nt_status ;
}
2006-01-12 12:33:49 +03:00
/* Now call the callback. This will
* do something like print the data or
* write to an ldb */
nt_status = r - > in . delta_fn ( delta_ctx ,
r - > in . fn_ctx ,
dbsync . in . database_id ,
& dbsync . out . delta_enum_array - > delta_enum [ d ] ,
& error_string ) ;
2005-05-03 18:38:14 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-01-13 15:52:56 +03:00
r - > out . error_string = talloc_steal ( mem_ctx , error_string ) ;
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-03 18:38:14 +04:00
return nt_status ;
2005-05-02 18:17:19 +04:00
}
2005-05-03 18:38:14 +04:00
talloc_free ( delta_ctx ) ;
2005-05-02 18:17:19 +04:00
}
2005-05-03 18:38:14 +04:00
talloc_free ( loop_ctx ) ;
} while ( NT_STATUS_EQUAL ( dbsync_nt_status , STATUS_MORE_ENTRIES ) ) ;
2006-01-13 15:52:56 +03:00
if ( ! NT_STATUS_IS_OK ( dbsync_nt_status ) ) {
r - > out . error_string = talloc_asprintf ( mem_ctx , " libnet_SamSync_netlogon failed: unexpected inconsistancy. Should not get error %s here " , nt_errstr ( nt_status ) ) ;
talloc_free ( samsync_ctx ) ;
return dbsync_nt_status ;
}
nt_status = NT_STATUS_OK ;
2005-05-03 18:38:14 +04:00
}
2005-07-25 06:23:41 +04:00
talloc_free ( samsync_ctx ) ;
2005-05-03 18:38:14 +04:00
return nt_status ;
}