2003-08-13 01:53:07 +00:00
/*
Unix SMB / CIFS implementation .
Authenticate to a remote server
Copyright ( C ) Andrew Tridgell 1992 - 1998
Copyright ( C ) Andrew Bartlett 2001
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
/****************************************************************************
Support for server level security .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-08-04 13:23:35 +00:00
static struct smbcli_state * server_cryptkey ( TALLOC_CTX * mem_ctx )
2003-08-13 01:53:07 +00:00
{
2004-08-04 13:23:35 +00:00
struct smbcli_state * cli = NULL ;
2003-08-13 01:53:07 +00:00
fstring desthost ;
2004-11-01 22:48:25 +00:00
struct ipv4_addr dest_ip ;
2003-08-13 01:53:07 +00:00
const char * p ;
char * pserver ;
BOOL connected_ok = False ;
2004-08-04 13:23:35 +00:00
if ( ! ( cli = smbcli_initialise ( cli ) ) )
2003-08-13 01:53:07 +00:00
return NULL ;
/* security = server just can't function with spnego */
cli - > use_spnego = False ;
pserver = talloc_strdup ( mem_ctx , lp_passwordserver ( ) ) ;
p = pserver ;
while ( next_token ( & p , desthost , LIST_SEP , sizeof ( desthost ) ) ) {
strupper ( desthost ) ;
if ( ! resolve_name ( desthost , & dest_ip , 0x20 ) ) {
DEBUG ( 1 , ( " server_cryptkey: Can't resolve address for %s \n " , desthost ) ) ;
continue ;
}
if ( ismyip ( dest_ip ) ) {
DEBUG ( 1 , ( " Password server loop - disabling password server %s \n " , desthost ) ) ;
continue ;
}
/* we use a mutex to prevent two connections at once - when a
Win2k PDC get two connections where one hasn ' t completed a
session setup yet it will send a TCP reset to the first
connection ( tridge ) */
if ( ! grab_server_mutex ( desthost ) ) {
return NULL ;
}
2004-08-04 13:23:35 +00:00
if ( smbcli_connect ( cli , desthost , & dest_ip ) ) {
2003-08-13 01:53:07 +00:00
DEBUG ( 3 , ( " connected to password server %s \n " , desthost ) ) ;
connected_ok = True ;
break ;
}
}
if ( ! connected_ok ) {
release_server_mutex ( ) ;
DEBUG ( 0 , ( " password server not available \n " ) ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return NULL ;
}
if ( ! attempt_netbios_session_request ( cli , lp_netbios_name ( ) ,
desthost , & dest_ip ) ) {
release_server_mutex ( ) ;
DEBUG ( 1 , ( " password server fails session request \n " ) ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return NULL ;
}
if ( strequal ( desthost , myhostname ( mem_ctx ) ) ) {
exit_server ( " Password server loop! " ) ;
}
DEBUG ( 3 , ( " got session \n " ) ) ;
2004-08-04 13:23:35 +00:00
if ( ! smbcli_negprot ( cli ) ) {
2003-08-13 01:53:07 +00:00
DEBUG ( 1 , ( " %s rejected the negprot \n " , desthost ) ) ;
release_server_mutex ( ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return NULL ;
}
if ( cli - > protocol < PROTOCOL_LANMAN2 | |
! ( cli - > sec_mode & NEGOTIATE_SECURITY_USER_LEVEL ) ) {
DEBUG ( 1 , ( " %s isn't in user level security mode \n " , desthost ) ) ;
release_server_mutex ( ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return NULL ;
}
/* Get the first session setup done quickly, to avoid silly
Win2k bugs . ( The next connection to the server will kill
this one . . .
*/
2004-08-04 13:23:35 +00:00
if ( ! smbcli_session_setup ( cli , " " , " " , 0 , " " , 0 ,
2003-08-13 01:53:07 +00:00
" " ) ) {
DEBUG ( 0 , ( " %s rejected the initial session setup (%s) \n " ,
2004-08-04 13:23:35 +00:00
desthost , smbcli_errstr ( cli ) ) ) ;
2003-08-13 01:53:07 +00:00
release_server_mutex ( ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return NULL ;
}
release_server_mutex ( ) ;
DEBUG ( 3 , ( " password server OK \n " ) ) ;
return cli ;
}
/****************************************************************************
Clean up our allocated cli .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void free_server_private_data ( void * * private_data_pointer )
{
2004-08-04 13:23:35 +00:00
struct smbcli_state * * cli = ( struct smbcli_state * * ) private_data_pointer ;
2003-08-13 01:53:07 +00:00
if ( * cli & & ( * cli ) - > initialised ) {
2005-06-18 00:02:36 +00:00
talloc_free ( * cli ) ;
2003-08-13 01:53:07 +00:00
}
}
/****************************************************************************
Get the challenge out of a password server .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static DATA_BLOB auth_get_challenge_server ( const struct auth_context * auth_context ,
void * * my_private_data ,
TALLOC_CTX * mem_ctx )
{
2004-08-04 13:23:35 +00:00
struct smbcli_state * cli = server_cryptkey ( mem_ctx ) ;
2003-08-13 01:53:07 +00:00
if ( cli ) {
DEBUG ( 3 , ( " using password server validation \n " ) ) ;
if ( ( cli - > sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE ) = = 0 ) {
/* We can't work with unencrypted password servers
unless ' encrypt passwords = no ' */
DEBUG ( 5 , ( " make_auth_info_server: Server is unencrypted, no challenge available.. \n " ) ) ;
/* However, it is still a perfectly fine connection
to pass that unencrypted password over */
* my_private_data = ( void * ) cli ;
return data_blob ( NULL , 0 ) ;
} else if ( cli - > secblob . length < 8 ) {
/* We can't do much if we don't get a full challenge */
DEBUG ( 2 , ( " make_auth_info_server: Didn't receive a full challenge from server \n " ) ) ;
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
return data_blob ( NULL , 0 ) ;
}
* my_private_data = ( void * ) cli ;
/* The return must be allocated on the caller's mem_ctx, as our own will be
destoyed just after the call . */
return data_blob_talloc ( auth_context - > mem_ctx , cli - > secblob . data , 8 ) ;
} else {
return data_blob ( NULL , 0 ) ;
}
}
/****************************************************************************
Check for a valid username and password in security = server mode .
- Validate a password with the password server .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS check_smbserver_security ( const struct auth_context * auth_context ,
void * my_private_data ,
TALLOC_CTX * mem_ctx ,
const auth_usersupplied_info * user_info ,
auth_serversupplied_info * * server_info )
{
2004-08-04 13:23:35 +00:00
struct smbcli_state * cli ;
2004-05-29 08:11:46 +00:00
static uint8_t badpass [ 24 ] ;
2003-08-13 01:53:07 +00:00
static fstring baduser ;
static BOOL tested_password_server = False ;
static BOOL bad_password_server = False ;
NTSTATUS nt_status = NT_STATUS_LOGON_FAILURE ;
BOOL locally_made_cli = False ;
/*
* Check that the requested domain is not our own machine name .
* If it is , we should never check the PDC here , we use our own local
* password file .
*/
if ( is_myname ( user_info - > domain . str ) ) {
DEBUG ( 3 , ( " check_smbserver_security: Requested domain was for this machine. \n " ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
cli = my_private_data ;
if ( cli ) {
} else {
cli = server_cryptkey ( mem_ctx ) ;
locally_made_cli = True ;
}
if ( ! cli | | ! cli - > initialised ) {
DEBUG ( 1 , ( " password server is not connected (cli not initilised) \n " ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
if ( ( cli - > sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE ) = = 0 ) {
if ( user_info - > encrypted ) {
DEBUG ( 1 , ( " password server %s is plaintext, but we are encrypted. This just can't work :-( \n " , cli - > desthost ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
} else {
if ( memcmp ( cli - > secblob . data , auth_context - > challenge . data , 8 ) ! = 0 ) {
DEBUG ( 1 , ( " the challenge that the password server (%s) supplied us is not the one we gave our client. This just can't work :-( \n " , cli - > desthost ) ) ;
return NT_STATUS_LOGON_FAILURE ;
}
}
if ( badpass [ 0 ] = = 0 )
memset ( badpass , 0x1f , sizeof ( badpass ) ) ;
if ( ( user_info - > nt_resp . length = = sizeof ( badpass ) ) & &
! memcmp ( badpass , user_info - > nt_resp . data , sizeof ( badpass ) ) ) {
/*
* Very unlikely , our random bad password is the same as the users
* password .
*/
memset ( badpass , badpass [ 0 ] + 1 , sizeof ( badpass ) ) ;
}
if ( baduser [ 0 ] = = 0 ) {
fstrcpy ( baduser , INVALID_USER_PREFIX ) ;
fstrcat ( baduser , lp_netbios_name ( ) ) ;
}
/*
* Attempt a session setup with a totally incorrect password .
* If this succeeds with the guest bit * NOT * set then the password
* server is broken and is not correctly setting the guest bit . We
* need to detect this as some versions of NT4 . x are broken . JRA .
*/
/* I sure as hell hope that there aren't servers out there that take
* NTLMv2 and have this bug , as we don ' t test for that . . .
* - abartlet @ samba . org
*/
if ( ( ! tested_password_server ) & & ( lp_paranoid_server_security ( ) ) ) {
2004-08-04 13:23:35 +00:00
if ( smbcli_session_setup ( cli , baduser , ( char * ) badpass , sizeof ( badpass ) ,
2003-08-13 01:53:07 +00:00
( char * ) badpass , sizeof ( badpass ) , user_info - > domain . str ) ) {
/*
* We connected to the password server so we
* can say we ' ve tested it .
*/
tested_password_server = True ;
if ( ( SVAL ( cli - > inbuf , smb_vwv2 ) & 1 ) = = 0 ) {
DEBUG ( 0 , ( " server_validate: password server %s allows users as non-guest \
with a bad password . \ n " , cli->desthost));
DEBUG ( 0 , ( " server_validate: This is broken (and insecure) behaviour. Please do not \
use this machine as the password server . \ n " ));
2004-08-04 13:23:35 +00:00
smbcli_ulogoff ( cli ) ;
2003-08-13 01:53:07 +00:00
/*
* Password server has the bug .
*/
bad_password_server = True ;
return NT_STATUS_LOGON_FAILURE ;
}
2004-08-04 13:23:35 +00:00
smbcli_ulogoff ( cli ) ;
2003-08-13 01:53:07 +00:00
}
} else {
/*
* We have already tested the password server .
* Fail immediately if it has the bug .
*/
if ( bad_password_server ) {
DEBUG ( 0 , ( " server_validate: [1] password server %s allows users as non-guest \
with a bad password . \ n " , cli->desthost));
DEBUG ( 0 , ( " server_validate: [1] This is broken (and insecure) behaviour. Please do not \
use this machine as the password server . \ n " ));
return NT_STATUS_LOGON_FAILURE ;
}
}
/*
* Now we know the password server will correctly set the guest bit , or is
* not guest enabled , we can try with the real password .
*/
if ( ! user_info - > encrypted ) {
/* Plaintext available */
2004-08-04 13:23:35 +00:00
if ( ! smbcli_session_setup ( cli , user_info - > smb_name . str ,
2003-08-13 01:53:07 +00:00
( char * ) user_info - > plaintext_password . data ,
user_info - > plaintext_password . length ,
NULL , 0 ,
user_info - > domain . str ) ) {
DEBUG ( 1 , ( " password server %s rejected the password \n " , cli - > desthost ) ) ;
2004-08-04 13:23:35 +00:00
/* Make this smbcli_nt_error() when the conversion is in */
nt_status = smbcli_nt_error ( cli ) ;
2003-08-13 01:53:07 +00:00
} else {
nt_status = NT_STATUS_OK ;
}
} else {
2004-08-04 13:23:35 +00:00
if ( ! smbcli_session_setup ( cli , user_info - > smb_name . str ,
2003-08-13 01:53:07 +00:00
( char * ) user_info - > lm_resp . data ,
user_info - > lm_resp . length ,
( char * ) user_info - > nt_resp . data ,
user_info - > nt_resp . length ,
user_info - > domain . str ) ) {
DEBUG ( 1 , ( " password server %s rejected the password \n " , cli - > desthost ) ) ;
2004-08-04 13:23:35 +00:00
/* Make this smbcli_nt_error() when the conversion is in */
nt_status = smbcli_nt_error ( cli ) ;
2003-08-13 01:53:07 +00:00
} else {
nt_status = NT_STATUS_OK ;
}
}
/* if logged in as guest then reject */
if ( ( SVAL ( cli - > inbuf , smb_vwv2 ) & 1 ) ! = 0 ) {
DEBUG ( 1 , ( " password server %s gave us guest only \n " , cli - > desthost ) ) ;
nt_status = NT_STATUS_LOGON_FAILURE ;
}
2004-08-04 13:23:35 +00:00
smbcli_ulogoff ( cli ) ;
2003-08-13 01:53:07 +00:00
if NT_STATUS_IS_OK ( nt_status ) {
struct passwd * pass = Get_Pwnam ( user_info - > internal_username . str ) ;
if ( pass ) {
2004-09-26 05:38:45 +00:00
nt_status = make_server_info_pw ( auth_context , server_info , pass ) ;
2003-08-13 01:53:07 +00:00
} else {
nt_status = NT_STATUS_NO_SUCH_USER ;
}
}
if ( locally_made_cli ) {
2005-06-18 00:02:36 +00:00
talloc_free ( cli ) ;
2003-08-13 01:53:07 +00:00
}
return ( nt_status ) ;
}
NTSTATUS auth_init_smbserver ( struct auth_context * auth_context , const char * param , auth_methods * * auth_method )
{
if ( ! make_auth_methods ( auth_context , auth_method ) ) {
return NT_STATUS_NO_MEMORY ;
}
( * auth_method ) - > name = " smbserver " ;
( * auth_method ) - > auth = check_smbserver_security ;
( * auth_method ) - > get_chal = auth_get_challenge_server ;
( * auth_method ) - > send_keepalive = send_server_keepalive ;
( * auth_method ) - > free_private_data = free_server_private_data ;
return NT_STATUS_OK ;
}