2006-08-19 05:04:54 +04:00
/*
Unix SMB / CIFS implementation .
Winbind daemon - cached credentials funcions
Copyright ( C ) Robert O ' Callahan 2006
Copyright ( C ) Jeremy Allison 2006 ( minor fixes to fit into Samba and
protect against integer wrap ) .
2009-12-21 17:11:55 +03:00
2006-08-19 05:04:54 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-08-19 05:04:54 +04:00
( at your option ) any later version .
2009-12-21 17:11:55 +03:00
2006-08-19 05:04:54 +04:00
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 .
2009-12-21 17:11:55 +03:00
2006-08-19 05:04:54 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-08-19 05:04:54 +04:00
*/
# include "includes.h"
# include "winbindd.h"
2009-12-22 10:50:55 +03:00
# include "ntlmssp.h"
2006-08-19 05:04:54 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_WINBIND
2007-10-19 04:40:25 +04:00
static bool client_can_access_ccache_entry ( uid_t client_uid ,
2006-08-29 03:01:30 +04:00
struct WINBINDD_MEMORY_CREDS * entry )
2006-08-19 05:04:54 +04:00
{
if ( client_uid = = entry - > uid | | client_uid = = 0 ) {
2009-05-12 08:56:57 +04:00
DEBUG ( 10 , ( " Access granted to uid %u \n " , ( unsigned int ) client_uid ) ) ;
2006-08-19 05:04:54 +04:00
return True ;
}
2009-05-12 08:56:57 +04:00
DEBUG ( 1 , ( " Access denied to uid %u (expected %u) \n " ,
( unsigned int ) client_uid , ( unsigned int ) entry - > uid ) ) ;
2006-08-19 05:04:54 +04:00
return False ;
}
2006-08-20 05:25:26 +04:00
static NTSTATUS do_ntlm_auth_with_hashes ( const char * username ,
2006-08-19 05:04:54 +04:00
const char * domain ,
2006-08-20 05:25:26 +04:00
const unsigned char lm_hash [ LM_HASH_LEN ] ,
const unsigned char nt_hash [ NT_HASH_LEN ] ,
2006-08-19 05:04:54 +04:00
const DATA_BLOB initial_msg ,
const DATA_BLOB challenge_msg ,
2010-01-24 18:41:30 +03:00
DATA_BLOB * auth_msg ,
uint8_t session_key [ 16 ] )
2006-08-19 05:04:54 +04:00
{
NTSTATUS status ;
2009-12-22 10:50:55 +03:00
struct ntlmssp_state * ntlmssp_state = NULL ;
2006-08-19 05:04:54 +04:00
DATA_BLOB dummy_msg , reply ;
2009-12-30 16:13:45 +03:00
status = ntlmssp_client_start ( NULL ,
global_myname ( ) ,
lp_workgroup ( ) ,
lp_client_ntlmv2_auth ( ) ,
& ntlmssp_state ) ;
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Could not start NTLMSSP client: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
status = ntlmssp_set_username ( ntlmssp_state , username ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Could not set username: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
status = ntlmssp_set_domain ( ntlmssp_state , domain ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Could not set domain: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
2006-08-20 05:25:26 +04:00
status = ntlmssp_set_hashes ( ntlmssp_state , lm_hash , nt_hash ) ;
2009-12-21 17:11:55 +03:00
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2006-08-20 05:25:26 +04:00
DEBUG ( 1 , ( " Could not set hashes: %s \n " ,
2006-08-19 05:04:54 +04:00
nt_errstr ( status ) ) ) ;
goto done ;
}
2010-01-24 18:41:30 +03:00
ntlmssp_want_feature ( ntlmssp_state , NTLMSSP_FEATURE_SESSION_KEY ) ;
2006-08-19 05:04:54 +04:00
/* We need to get our protocol handler into the right state. So first
we ask it to generate the initial message . Actually the client has already
sent its own initial message , so we ' re going to drop this one on the floor .
The client might have sent a different message , for example with different
negotiation options , but as far as I can tell this won ' t hurt us . ( Unless
the client sent a different username or domain , in which case that ' s their
problem for telling us the wrong username or domain . )
Since we have a copy of the initial message that the client sent , we could
resolve any discrepancies if we had to .
*/
2007-05-14 16:16:20 +04:00
dummy_msg = data_blob_null ;
reply = data_blob_null ;
2006-08-19 05:04:54 +04:00
status = ntlmssp_update ( ntlmssp_state , dummy_msg , & reply ) ;
data_blob_free ( & dummy_msg ) ;
data_blob_free ( & reply ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
DEBUG ( 1 , ( " Failed to create initial message! [%s] \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
/* Now we are ready to handle the server's actual response. */
status = ntlmssp_update ( ntlmssp_state , challenge_msg , & reply ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_OK ) ) {
DEBUG ( 1 , ( " We didn't get a response to the challenge! [%s] \n " ,
nt_errstr ( status ) ) ) ;
data_blob_free ( & reply ) ;
goto done ;
}
2010-01-24 18:41:30 +03:00
if ( ntlmssp_state - > session_key . length ! = 16 ) {
DEBUG ( 1 , ( " invalid session key length %d \n " ,
( int ) ntlmssp_state - > session_key . length ) ) ;
data_blob_free ( & reply ) ;
goto done ;
}
2010-01-09 22:22:00 +03:00
* auth_msg = data_blob ( reply . data , reply . length ) ;
2010-01-24 18:41:30 +03:00
memcpy ( session_key , ntlmssp_state - > session_key . data , 16 ) ;
2006-08-19 05:04:54 +04:00
status = NT_STATUS_OK ;
done :
ntlmssp_end ( & ntlmssp_state ) ;
return status ;
}
2007-10-19 04:40:25 +04:00
static bool check_client_uid ( struct winbindd_cli_state * state , uid_t uid )
2006-08-19 05:04:54 +04:00
{
int ret ;
uid_t ret_uid ;
ret_uid = ( uid_t ) - 1 ;
2006-08-21 00:05:49 +04:00
ret = sys_getpeereid ( state - > sock , & ret_uid ) ;
2006-08-19 05:04:54 +04:00
if ( ret ! = 0 ) {
DEBUG ( 1 , ( " check_client_uid: Could not get socket peer uid: %s; "
" denying access \n " , strerror ( errno ) ) ) ;
return False ;
}
if ( uid ! = ret_uid ) {
2009-05-12 08:56:57 +04:00
DEBUG ( 1 , ( " check_client_uid: Client lied about its uid: said %u, "
" actually was %u; denying access \n " ,
( unsigned int ) uid , ( unsigned int ) ret_uid ) ) ;
2006-08-19 05:04:54 +04:00
return False ;
}
return True ;
}
void winbindd_ccache_ntlm_auth ( struct winbindd_cli_state * state )
{
struct winbindd_domain * domain ;
fstring name_domain , name_user ;
2010-04-18 16:14:43 +04:00
NTSTATUS result = NT_STATUS_NOT_SUPPORTED ;
struct WINBINDD_MEMORY_CREDS * entry ;
DATA_BLOB initial , challenge , auth ;
uint32 initial_blob_len , challenge_blob_len , extra_len ;
2006-08-19 05:04:54 +04:00
/* Ensure null termination */
2009-05-08 00:46:27 +04:00
state - > request - > data . ccache_ntlm_auth . user [
sizeof ( state - > request - > data . ccache_ntlm_auth . user ) - 1 ] = ' \0 ' ;
2006-08-19 05:04:54 +04:00
DEBUG ( 3 , ( " [%5lu]: perform NTLM auth on behalf of user %s \n " , ( unsigned long ) state - > pid ,
2009-05-08 00:46:27 +04:00
state - > request - > data . ccache_ntlm_auth . user ) ) ;
2006-08-19 05:04:54 +04:00
/* Parse domain and username */
2009-05-08 00:46:27 +04:00
if ( ! canonicalize_username ( state - > request - > data . ccache_ntlm_auth . user ,
2006-08-19 05:04:54 +04:00
name_domain , name_user ) ) {
DEBUG ( 5 , ( " winbindd_ccache_ntlm_auth: cannot parse domain and user from name [%s] \n " ,
2009-05-08 00:46:27 +04:00
state - > request - > data . ccache_ntlm_auth . user ) ) ;
2006-08-19 05:04:54 +04:00
request_error ( state ) ;
return ;
}
2009-09-27 13:49:11 +04:00
domain = find_auth_domain ( state - > request - > flags , name_domain ) ;
2006-08-19 05:04:54 +04:00
if ( domain = = NULL ) {
DEBUG ( 5 , ( " winbindd_ccache_ntlm_auth: can't get domain [%s] \n " ,
name_domain ) ) ;
request_error ( state ) ;
return ;
}
2009-05-08 00:46:27 +04:00
if ( ! check_client_uid ( state , state - > request - > data . ccache_ntlm_auth . uid ) ) {
2006-08-19 05:04:54 +04:00
request_error ( state ) ;
return ;
}
/* validate blob lengths */
2009-05-08 00:46:27 +04:00
initial_blob_len = state - > request - > data . ccache_ntlm_auth . initial_blob_len ;
challenge_blob_len = state - > request - > data . ccache_ntlm_auth . challenge_blob_len ;
extra_len = state - > request - > extra_len ;
2006-08-19 05:04:54 +04:00
if ( initial_blob_len > extra_len | | challenge_blob_len > extra_len | |
initial_blob_len + challenge_blob_len > extra_len | |
initial_blob_len + challenge_blob_len < initial_blob_len | |
initial_blob_len + challenge_blob_len < challenge_blob_len ) {
DEBUG ( 10 , ( " winbindd_dual_ccache_ntlm_auth: blob lengths overrun "
" or wrap. Buffer [%d+%d > %d] \n " ,
initial_blob_len ,
challenge_blob_len ,
extra_len ) ) ;
goto process_result ;
}
/* Parse domain and username */
2009-05-08 00:46:27 +04:00
if ( ! parse_domain_user ( state - > request - > data . ccache_ntlm_auth . user , name_domain , name_user ) ) {
2006-08-19 05:04:54 +04:00
DEBUG ( 10 , ( " winbindd_dual_ccache_ntlm_auth: cannot parse "
" domain and user from name [%s] \n " ,
2009-05-08 00:46:27 +04:00
state - > request - > data . ccache_ntlm_auth . user ) ) ;
2006-08-19 05:04:54 +04:00
goto process_result ;
}
2009-05-08 00:46:27 +04:00
entry = find_memory_creds_by_name ( state - > request - > data . ccache_ntlm_auth . user ) ;
2006-08-29 03:01:30 +04:00
if ( entry = = NULL | | entry - > nt_hash = = NULL | | entry - > lm_hash = = NULL ) {
2006-08-19 05:04:54 +04:00
DEBUG ( 10 , ( " winbindd_dual_ccache_ntlm_auth: could not find "
" credentials for user %s \n " ,
2009-05-08 00:46:27 +04:00
state - > request - > data . ccache_ntlm_auth . user ) ) ;
2006-08-19 05:04:54 +04:00
goto process_result ;
}
2006-08-29 03:01:30 +04:00
DEBUG ( 10 , ( " winbindd_dual_ccache_ntlm_auth: found ccache [%s] \n " , entry - > username ) ) ;
2006-08-19 05:04:54 +04:00
2009-05-08 00:46:27 +04:00
if ( ! client_can_access_ccache_entry ( state - > request - > data . ccache_ntlm_auth . uid , entry ) ) {
2006-08-19 05:04:54 +04:00
goto process_result ;
}
if ( initial_blob_len = = 0 & & challenge_blob_len = = 0 ) {
/* this is just a probe to see if credentials are available. */
result = NT_STATUS_OK ;
2009-06-14 14:41:46 +04:00
state - > response - > data . ccache_ntlm_auth . auth_blob_len = 0 ;
2006-08-19 05:04:54 +04:00
goto process_result ;
}
2009-12-20 02:27:34 +03:00
initial = data_blob_const ( state - > request - > extra_data . data ,
initial_blob_len ) ;
challenge = data_blob_const (
state - > request - > extra_data . data + initial_blob_len ,
state - > request - > data . ccache_ntlm_auth . challenge_blob_len ) ;
2010-01-24 18:41:30 +03:00
result = do_ntlm_auth_with_hashes (
name_user , name_domain , entry - > lm_hash , entry - > nt_hash ,
initial , challenge , & auth ,
state - > response - > data . ccache_ntlm_auth . session_key ) ;
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( result ) ) {
goto process_result ;
}
2009-06-14 14:41:46 +04:00
state - > response - > extra_data . data = talloc_memdup (
2009-05-12 19:47:22 +04:00
state - > mem_ctx , auth . data , auth . length ) ;
2009-06-14 14:41:46 +04:00
if ( ! state - > response - > extra_data . data ) {
2006-08-19 05:04:54 +04:00
result = NT_STATUS_NO_MEMORY ;
goto process_result ;
}
2009-06-14 14:41:46 +04:00
state - > response - > length + = auth . length ;
state - > response - > data . ccache_ntlm_auth . auth_blob_len = auth . length ;
2006-08-19 05:04:54 +04:00
data_blob_free ( & auth ) ;
process_result :
2010-04-18 16:14:43 +04:00
if ( ! NT_STATUS_IS_OK ( result ) ) {
request_error ( state ) ;
return ;
}
request_ok ( state ) ;
2006-08-19 05:04:54 +04:00
}
2010-01-09 22:20:36 +03:00
void winbindd_ccache_save ( struct winbindd_cli_state * state )
{
struct winbindd_domain * domain ;
fstring name_domain , name_user ;
2010-04-18 16:14:43 +04:00
NTSTATUS status ;
2010-01-09 22:20:36 +03:00
/* Ensure null termination */
state - > request - > data . ccache_save . user [
sizeof ( state - > request - > data . ccache_save . user ) - 1 ] = ' \0 ' ;
state - > request - > data . ccache_save . pass [
sizeof ( state - > request - > data . ccache_save . pass ) - 1 ] = ' \0 ' ;
2010-01-28 17:10:54 +03:00
DEBUG ( 3 , ( " [%5lu]: save password of user %s \n " ,
2010-01-09 22:20:36 +03:00
( unsigned long ) state - > pid ,
state - > request - > data . ccache_save . user ) ) ;
/* Parse domain and username */
2010-04-17 21:24:34 +04:00
if ( ! canonicalize_username ( state - > request - > data . ccache_save . user ,
2010-01-09 22:20:36 +03:00
name_domain , name_user ) ) {
DEBUG ( 5 , ( " winbindd_ccache_save: cannot parse domain and user "
" from name [%s] \n " ,
state - > request - > data . ccache_save . user ) ) ;
request_error ( state ) ;
return ;
}
2010-04-18 16:14:43 +04:00
/*
* The domain is checked here only for compatibility
* reasons . We used to do the winbindd memory ccache for
* ntlm_auth in the domain child . With that code , we had to
* make sure that we do have a domain around to send this
* to . Now we do the memory cache in the parent winbindd ,
* where it would not matter if we have a domain or not .
*/
2010-01-09 22:20:36 +03:00
2010-04-18 16:14:43 +04:00
domain = find_auth_domain ( state - > request - > flags , name_domain ) ;
2010-01-09 22:20:36 +03:00
if ( domain = = NULL ) {
DEBUG ( 5 , ( " winbindd_ccache_save: can't get domain [%s] \n " ,
name_domain ) ) ;
request_error ( state ) ;
return ;
}
if ( ! check_client_uid ( state , state - > request - > data . ccache_save . uid ) ) {
request_error ( state ) ;
return ;
}
status = winbindd_add_memory_creds (
state - > request - > data . ccache_save . user ,
state - > request - > data . ccache_save . uid ,
state - > request - > data . ccache_save . pass ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " winbindd_add_memory_creds failed %s \n " ,
nt_errstr ( status ) ) ) ;
2010-04-18 16:14:43 +04:00
request_error ( state ) ;
return ;
2010-01-09 22:20:36 +03:00
}
2010-04-18 16:14:43 +04:00
request_ok ( state ) ;
2010-01-09 22:20:36 +03:00
}