2008-09-22 21:29:05 +04:00
/*
Unix SMB / CIFS implementation .
Authenticate against a netlogon pipe listening on a unix domain socket
Copyright ( C ) Volker Lendecke 2008
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"
2011-03-25 04:28:05 +03:00
# include "auth.h"
2009-03-16 13:27:58 +03:00
# include "../libcli/auth/libcli_auth.h"
2010-05-05 03:39:16 +04:00
# include "../librpc/gen_ndr/ndr_netlogon.h"
2011-02-06 17:50:04 +03:00
# include "librpc/gen_ndr/ndr_schannel.h"
2011-02-28 12:19:44 +03:00
# include "rpc_client/cli_pipe.h"
2010-05-18 20:26:03 +04:00
# include "rpc_client/cli_netlogon.h"
2010-08-05 04:25:37 +04:00
# include "secrets.h"
2011-02-05 16:42:01 +03:00
# include "tldap.h"
# include "tldap_util.h"
2008-09-22 21:29:05 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_AUTH
2011-02-06 17:50:04 +03:00
static bool secrets_store_local_schannel_creds (
const struct netlogon_creds_CredentialState * creds )
{
DATA_BLOB blob ;
enum ndr_err_code ndr_err ;
bool ret ;
ndr_err = ndr_push_struct_blob (
& blob , talloc_tos ( ) , creds ,
( ndr_push_flags_fn_t ) ndr_push_netlogon_creds_CredentialState ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_push_netlogon_creds_CredentialState failed: "
" %s \n " , ndr_errstr ( ndr_err ) ) ) ;
return false ;
}
ret = secrets_store ( SECRETS_LOCAL_SCHANNEL_KEY ,
blob . data , blob . length ) ;
data_blob_free ( & blob ) ;
return ret ;
}
static struct netlogon_creds_CredentialState *
secrets_fetch_local_schannel_creds ( TALLOC_CTX * mem_ctx )
{
struct netlogon_creds_CredentialState * creds ;
enum ndr_err_code ndr_err ;
DATA_BLOB blob ;
blob . data = ( uint8_t * ) secrets_fetch ( SECRETS_LOCAL_SCHANNEL_KEY ,
& blob . length ) ;
if ( blob . data = = NULL ) {
DEBUG ( 10 , ( " secrets_fetch failed \n " ) ) ;
return NULL ;
}
creds = talloc ( mem_ctx , struct netlogon_creds_CredentialState ) ;
if ( creds = = NULL ) {
DEBUG ( 10 , ( " talloc failed \n " ) ) ;
SAFE_FREE ( blob . data ) ;
return NULL ;
}
ndr_err = ndr_pull_struct_blob (
& blob , creds , creds ,
( ndr_pull_flags_fn_t ) ndr_pull_netlogon_creds_CredentialState ) ;
SAFE_FREE ( blob . data ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_pull_netlogon_creds_CredentialState failed: "
" %s \n " , ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( creds ) ;
return NULL ;
}
return creds ;
}
2008-09-22 21:29:05 +04:00
static NTSTATUS netlogond_validate ( TALLOC_CTX * mem_ctx ,
const struct auth_context * auth_context ,
const char * ncalrpc_sockname ,
2011-02-06 17:50:04 +03:00
struct netlogon_creds_CredentialState * creds ,
2010-01-10 16:16:04 +03:00
const struct auth_usersupplied_info * user_info ,
2008-09-22 21:29:05 +04:00
struct netr_SamInfo3 * * pinfo3 ,
NTSTATUS * schannel_bind_result )
{
2009-11-13 00:56:33 +03:00
struct rpc_pipe_client * p = NULL ;
2010-07-18 00:58:50 +04:00
struct pipe_auth_data * auth = NULL ;
2008-09-22 21:29:05 +04:00
struct netr_SamInfo3 * info3 = NULL ;
NTSTATUS status ;
* schannel_bind_result = NT_STATUS_OK ;
status = rpc_pipe_open_ncalrpc ( talloc_tos ( ) , ncalrpc_sockname ,
& ndr_table_netlogon . syntax_id , & p ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpc_pipe_open_ncalrpc failed: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
2011-02-06 17:50:04 +03:00
p - > dc = creds ;
2009-09-16 02:26:17 +04:00
2008-09-22 21:29:05 +04:00
status = rpccli_schannel_bind_data ( p , lp_workgroup ( ) ,
2009-09-14 22:39:54 +04:00
DCERPC_AUTH_LEVEL_PRIVACY ,
2009-09-16 02:26:17 +04:00
p - > dc , & auth ) ;
2008-09-22 21:29:05 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpccli_schannel_bind_data failed: %s \n " ,
nt_errstr ( status ) ) ) ;
TALLOC_FREE ( p ) ;
return status ;
}
status = rpc_pipe_bind ( p , auth ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpc_pipe_bind failed: %s \n " , nt_errstr ( status ) ) ) ;
TALLOC_FREE ( p ) ;
* schannel_bind_result = status ;
return status ;
}
status = rpccli_netlogon_sam_network_logon_ex (
p , p ,
2010-06-01 05:23:50 +04:00
user_info - > logon_parameters , /* flags such as 'allow
* workstation logon ' */
2011-06-09 09:31:03 +04:00
lp_netbios_name ( ) , /* server name */
2010-06-01 15:53:42 +04:00
user_info - > client . account_name , /* user name logging on. */
user_info - > client . domain_name , /* domain name */
2010-06-01 05:23:50 +04:00
user_info - > workstation_name , /* workstation name */
2008-09-22 21:29:05 +04:00
( uchar * ) auth_context - > challenge . data , /* 8 byte challenge. */
2011-01-07 19:28:29 +03:00
3 , /* validation level */
2010-06-01 15:52:01 +04:00
user_info - > password . response . lanman , /* lanman 24 byte response */
user_info - > password . response . nt , /* nt 24 byte response */
2010-06-01 05:23:50 +04:00
& info3 ) ; /* info3 out */
2008-09-22 21:29:05 +04:00
DEBUG ( 10 , ( " rpccli_netlogon_sam_network_logon_ex returned %s \n " ,
nt_errstr ( status ) ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( p ) ;
return status ;
}
* pinfo3 = talloc_move ( mem_ctx , & info3 ) ;
TALLOC_FREE ( p ) ;
return NT_STATUS_OK ;
}
2011-02-05 16:42:01 +03:00
static NTSTATUS get_ldapi_ctx ( TALLOC_CTX * mem_ctx , struct tldap_context * * pld )
2008-09-22 21:29:05 +04:00
{
2011-02-05 16:42:01 +03:00
struct tldap_context * ld ;
struct sockaddr_un addr ;
char * sockaddr ;
int fd ;
NTSTATUS status ;
int res ;
sockaddr = talloc_asprintf ( talloc_tos ( ) , " /%s/ldap_priv/ldapi " ,
lp_private_dir ( ) ) ;
if ( sockaddr = = NULL ) {
DEBUG ( 10 , ( " talloc failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
2008-09-22 21:29:05 +04:00
}
2011-02-05 16:42:01 +03:00
ZERO_STRUCT ( addr ) ;
addr . sun_family = AF_UNIX ;
strncpy ( addr . sun_path , sockaddr , sizeof ( addr . sun_path ) ) ;
TALLOC_FREE ( sockaddr ) ;
status = open_socket_out ( ( struct sockaddr_storage * ) ( void * ) & addr ,
0 , 0 , & fd ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " Could not connect to %s: %s \n " , addr . sun_path ,
nt_errstr ( status ) ) ) ;
return status ;
}
set_blocking ( fd , false ) ;
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
ld = tldap_context_create ( mem_ctx , fd ) ;
if ( ld = = NULL ) {
close ( fd ) ;
return NT_STATUS_NO_MEMORY ;
}
res = tldap_fetch_rootdse ( ld ) ;
if ( res ! = TLDAP_SUCCESS ) {
DEBUG ( 10 , ( " tldap_fetch_rootdse failed: %s \n " ,
tldap_errstr ( talloc_tos ( ) , ld , res ) ) ) ;
TALLOC_FREE ( ld ) ;
return NT_STATUS_LDAP ( res ) ;
2008-09-22 21:29:05 +04:00
}
2011-02-05 16:42:01 +03:00
* pld = ld ;
return NT_STATUS_OK ; ;
}
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
static NTSTATUS mymachinepw ( uint8_t pwd [ 16 ] )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2011-03-23 13:08:46 +03:00
struct tldap_context * ld = NULL ;
2011-02-05 16:42:01 +03:00
struct tldap_message * rootdse , * * msg ;
const char * attrs [ 1 ] = { " unicodePwd " } ;
char * default_nc , * myname ;
int rc , num_msg ;
DATA_BLOB pwdblob ;
NTSTATUS status ;
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
status = get_ldapi_ctx ( talloc_tos ( ) , & ld ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
2008-09-22 21:29:05 +04:00
}
2011-02-05 16:42:01 +03:00
rootdse = tldap_rootdse ( ld ) ;
if ( rootdse = = NULL ) {
DEBUG ( 10 , ( " Could not get rootdse \n " ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto fail ;
}
default_nc = tldap_talloc_single_attribute (
rootdse , " defaultNamingContext " , talloc_tos ( ) ) ;
if ( default_nc = = NULL ) {
DEBUG ( 10 , ( " Could not get defaultNamingContext \n " ) ) ;
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
DEBUG ( 10 , ( " default_nc = %s \n " , default_nc ) ) ;
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
myname = talloc_asprintf_strupper_m ( talloc_tos ( ) , " %s$ " ,
2011-06-09 09:31:03 +04:00
lp_netbios_name ( ) ) ;
2011-02-05 16:42:01 +03:00
if ( myname = = NULL ) {
DEBUG ( 10 , ( " talloc failed \n " ) ) ;
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
rc = tldap_search_fmt (
ld , default_nc , TLDAP_SCOPE_SUB , attrs , ARRAY_SIZE ( attrs ) , 0 ,
talloc_tos ( ) , & msg ,
" (&(sAMAccountName=%s)(objectClass=computer)) " , myname ) ;
if ( rc ! = TLDAP_SUCCESS ) {
DEBUG ( 10 , ( " Could not retrieve our account: %s \n " ,
tldap_errstr ( talloc_tos ( ) , ld , rc ) ) ) ;
status = NT_STATUS_LDAP ( rc ) ;
goto fail ;
2008-09-22 21:29:05 +04:00
}
2011-02-05 16:42:01 +03:00
num_msg = talloc_array_length ( msg ) ;
if ( num_msg ! = 1 ) {
DEBUG ( 10 , ( " Got %d accounts, expected one \n " , num_msg ) ) ;
status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
goto fail ;
}
if ( ! tldap_get_single_valueblob ( msg [ 0 ] , " unicodePwd " , & pwdblob ) ) {
char * dn = NULL ;
tldap_entry_dn ( msg [ 0 ] , & dn ) ;
DEBUG ( 10 , ( " No unicodePwd attribute in %s \n " ,
dn ? dn : " <unknown DN> " ) ) ;
status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
goto fail ;
}
if ( pwdblob . length ! = 16 ) {
DEBUG ( 10 , ( " Password hash hash has length %d, expected 16 \n " ,
( int ) pwdblob . length ) ) ;
status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
goto fail ;
}
memcpy ( pwd , pwdblob . data , 16 ) ;
2008-09-22 21:29:05 +04:00
2011-02-05 16:42:01 +03:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
2008-09-22 21:29:05 +04:00
}
static NTSTATUS check_netlogond_security ( const struct auth_context * auth_context ,
void * my_private_data ,
TALLOC_CTX * mem_ctx ,
2010-01-10 16:16:04 +03:00
const struct auth_usersupplied_info * user_info ,
2010-01-10 16:24:22 +03:00
struct auth_serversupplied_info * * server_info )
2008-09-22 21:29:05 +04:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct netr_SamInfo3 * info3 = NULL ;
2009-11-13 00:56:33 +03:00
struct rpc_pipe_client * p = NULL ;
2010-07-18 00:58:50 +04:00
struct pipe_auth_data * auth = NULL ;
2008-09-22 21:29:05 +04:00
uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS ;
uint8_t machine_password [ 16 ] ;
2011-02-06 17:50:04 +03:00
struct netlogon_creds_CredentialState * creds ;
2008-09-22 21:29:05 +04:00
NTSTATUS schannel_bind_result , status ;
2009-11-13 00:56:33 +03:00
struct named_mutex * mutex = NULL ;
2008-09-22 21:29:05 +04:00
const char * ncalrpcsock ;
2010-06-01 14:30:56 +04:00
DEBUG ( 10 , ( " Check auth for: [%s] \n " , user_info - > mapped . account_name ) ) ;
2010-05-30 00:49:37 +04:00
2008-09-22 21:29:05 +04:00
ncalrpcsock = lp_parm_const_string (
GLOBAL_SECTION_SNUM , " auth_netlogond " , " socket " , NULL ) ;
if ( ncalrpcsock = = NULL ) {
ncalrpcsock = talloc_asprintf ( talloc_tos ( ) , " %s/%s " ,
get_dyn_NCALRPCDIR ( ) , " DEFAULT " ) ;
}
if ( ncalrpcsock = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
2011-02-06 17:50:04 +03:00
creds = secrets_fetch_local_schannel_creds ( talloc_tos ( ) ) ;
if ( creds = = NULL ) {
2008-09-22 21:29:05 +04:00
goto new_key ;
}
status = netlogond_validate ( talloc_tos ( ) , auth_context , ncalrpcsock ,
2011-02-06 17:50:04 +03:00
creds , user_info , & info3 ,
2008-09-22 21:29:05 +04:00
& schannel_bind_result ) ;
DEBUG ( 10 , ( " netlogond_validate returned %s \n " , nt_errstr ( status ) ) ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
goto okay ;
}
if ( NT_STATUS_IS_OK ( schannel_bind_result ) ) {
/*
* This is a real failure from the DC
*/
goto done ;
}
new_key :
mutex = grab_named_mutex ( talloc_tos ( ) , " LOCAL_SCHANNEL_KEY " , 60 ) ;
if ( mutex = = NULL ) {
DEBUG ( 10 , ( " Could not get mutex LOCAL_SCHANNEL_KEY \n " ) ) ;
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
DEBUG ( 10 , ( " schannel bind failed, setting up new key \n " ) ) ;
status = rpc_pipe_open_ncalrpc ( talloc_tos ( ) , ncalrpcsock ,
& ndr_table_netlogon . syntax_id , & p ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpc_pipe_open_ncalrpc failed: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
status = rpccli_anon_bind_data ( p , & auth ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpccli_anon_bind_data failed: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
status = rpc_pipe_bind ( p , auth ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpc_pipe_bind failed: %s \n " , nt_errstr ( status ) ) ) ;
goto done ;
}
2011-02-05 16:42:01 +03:00
status = mymachinepw ( machine_password ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " mymachinepw failed: %s \n " , nt_errstr ( status ) ) ) ;
2008-09-22 21:29:05 +04:00
goto done ;
}
2011-02-06 17:50:04 +03:00
DEBUG ( 10 , ( " machinepw " ) ) ;
dump_data ( 10 , machine_password , 16 ) ;
2008-09-22 21:29:05 +04:00
status = rpccli_netlogon_setup_creds (
2011-06-09 09:31:03 +04:00
p , lp_netbios_name ( ) , lp_workgroup ( ) , lp_netbios_name ( ) ,
lp_netbios_name ( ) , machine_password , SEC_CHAN_BDC , & neg_flags ) ;
2008-09-22 21:29:05 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " rpccli_netlogon_setup_creds failed: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
2011-02-06 17:50:04 +03:00
secrets_store_local_schannel_creds ( p - > dc ) ;
2008-09-22 21:29:05 +04:00
/*
* Retry the authentication with the mutex held . This way nobody else
* can step on our toes .
*/
status = netlogond_validate ( talloc_tos ( ) , auth_context , ncalrpcsock ,
2011-02-06 21:56:22 +03:00
p - > dc , user_info , & info3 ,
2008-09-22 21:29:05 +04:00
& schannel_bind_result ) ;
2011-02-06 17:50:04 +03:00
TALLOC_FREE ( p ) ;
2008-09-22 21:29:05 +04:00
DEBUG ( 10 , ( " netlogond_validate returned %s \n " , nt_errstr ( status ) ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
okay :
2010-06-01 14:27:03 +04:00
status = make_server_info_info3 ( mem_ctx , user_info - > client . account_name ,
2010-06-01 15:11:14 +04:00
user_info - > mapped . domain_name , server_info ,
2008-09-22 21:29:05 +04:00
info3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " make_server_info_info3 failed: %s \n " ,
nt_errstr ( status ) ) ) ;
TALLOC_FREE ( frame ) ;
return status ;
}
status = NT_STATUS_OK ;
done :
TALLOC_FREE ( frame ) ;
return status ;
}
/* module initialisation */
static NTSTATUS auth_init_netlogond ( struct auth_context * auth_context ,
const char * param ,
auth_methods * * auth_method )
{
2010-04-11 14:37:48 +04:00
struct auth_methods * result ;
2011-06-07 05:44:43 +04:00
result = talloc_zero ( auth_context , struct auth_methods ) ;
2010-04-11 14:37:48 +04:00
if ( result = = NULL ) {
2008-09-22 21:29:05 +04:00
return NT_STATUS_NO_MEMORY ;
}
2010-04-11 14:37:48 +04:00
result - > name = " netlogond " ;
result - > auth = check_netlogond_security ;
2008-09-22 21:29:05 +04:00
2010-04-11 14:37:48 +04:00
* auth_method = result ;
2008-09-22 21:29:05 +04:00
return NT_STATUS_OK ;
}
NTSTATUS auth_netlogond_init ( void )
{
smb_register_auth ( AUTH_INTERFACE_VERSION , " netlogond " ,
auth_init_netlogond ) ;
return NT_STATUS_OK ;
}