2006-08-19 05:04:54 +04:00
/*
Unix SMB / CIFS implementation .
2023-07-18 12:45:25 +03:00
Winbind daemon - cached credentials functions
2006-08-19 05:04:54 +04:00
Copyright ( C ) Robert O ' Callahan 2006
Copyright ( C ) Jeremy Allison 2006 ( minor fixes to fit into Samba and
protect against integer wrap ) .
2011-12-27 07:59:17 +04:00
Copyright ( C ) Andrew Bartlett 2011
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"
2011-12-27 07:59:17 +04:00
# include "auth/gensec/gensec.h"
# include "auth_generic.h"
2023-10-21 14:06:10 +03:00
# include "lib/util/string_wrappers.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 ;
}
2018-04-26 13:17:12 +03:00
static NTSTATUS do_ntlm_auth_with_stored_pw ( const char * namespace ,
2011-12-27 07:59:17 +04:00
const char * domain ,
2018-04-26 13:17:12 +03:00
const char * username ,
2011-12-27 07:59:17 +04:00
const char * password ,
const DATA_BLOB initial_msg ,
const DATA_BLOB challenge_msg ,
2015-12-15 11:07:33 +03:00
TALLOC_CTX * mem_ctx ,
2011-12-27 07:59:17 +04:00
DATA_BLOB * auth_msg ,
2015-11-20 16:06:18 +03:00
uint8_t session_key [ 16 ] ,
uint8_t * new_spnego )
2006-08-19 05:04:54 +04:00
{
NTSTATUS status ;
2011-12-27 07:59:17 +04:00
struct auth_generic_state * auth_generic_state = NULL ;
2015-11-26 13:46:52 +03:00
DATA_BLOB reply , session_key_blob ;
2006-08-19 05:04:54 +04:00
2015-12-15 11:07:33 +03:00
status = auth_generic_client_prepare ( mem_ctx , & auth_generic_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 ;
}
2011-12-27 07:59:17 +04:00
status = auth_generic_set_username ( auth_generic_state , username ) ;
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Could not set username: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
2011-12-27 07:59:17 +04:00
status = auth_generic_set_domain ( auth_generic_state , domain ) ;
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 1 , ( " Could not set domain: %s \n " ,
nt_errstr ( status ) ) ) ;
goto done ;
}
2011-12-27 07:59:17 +04:00
status = auth_generic_set_password ( auth_generic_state , password ) ;
2009-12-21 17:11:55 +03:00
2006-08-19 05:04:54 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-12-27 07:59:17 +04:00
DEBUG ( 1 , ( " Could not set password: %s \n " ,
2006-08-19 05:04:54 +04:00
nt_errstr ( status ) ) ) ;
goto done ;
}
2015-11-26 13:46:52 +03:00
if ( initial_msg . length = = 0 ) {
gensec_want_feature ( auth_generic_state - > gensec_security ,
GENSEC_FEATURE_SESSION_KEY ) ;
}
2011-12-27 07:59:17 +04:00
2015-11-26 13:46:52 +03:00
status = auth_generic_client_start_by_name ( auth_generic_state ,
" ntlmssp_resume_ccache " ) ;
2011-12-27 07:59:17 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2015-11-26 13:46:52 +03:00
DEBUG ( 1 , ( " Could not start NTLMSSP resume mech: %s \n " ,
2011-12-27 07:59:17 +04:00
nt_errstr ( status ) ) ) ;
goto done ;
}
2010-01-24 18:41:30 +03:00
2015-11-26 13:46:52 +03:00
/*
2018-11-28 01:10:17 +03:00
* We inject the initial NEGOTIATE message our caller used
* in order to get the state machine into the correct position .
2015-11-26 13:46:52 +03:00
*/
2007-05-14 16:16:20 +04:00
reply = data_blob_null ;
2011-12-27 07:59:17 +04:00
status = gensec_update ( auth_generic_state - > gensec_security ,
2015-11-26 13:46:52 +03:00
talloc_tos ( ) , initial_msg , & reply ) ;
2006-08-19 05:04:54 +04:00
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. */
2011-12-27 07:59:17 +04:00
status = gensec_update ( auth_generic_state - > gensec_security ,
2015-12-15 11:07:33 +03:00
mem_ctx , challenge_msg , & reply ) ;
2006-08-19 05:04:54 +04:00
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
2011-12-27 07:59:17 +04:00
status = gensec_session_key ( auth_generic_state - > gensec_security ,
talloc_tos ( ) , & session_key_blob ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_OK ) ) {
DEBUG ( 1 , ( " We didn't get the session key we requested! [%s] \n " ,
nt_errstr ( status ) ) ) ;
2010-01-24 18:41:30 +03:00
data_blob_free ( & reply ) ;
goto done ;
}
2011-12-27 07:59:17 +04:00
if ( session_key_blob . length ! = 16 ) {
DEBUG ( 1 , ( " invalid session key length %d \n " ,
( int ) session_key_blob . length ) ) ;
data_blob_free ( & reply ) ;
goto done ;
}
memcpy ( session_key , session_key_blob . data , 16 ) ;
data_blob_free ( & session_key_blob ) ;
* auth_msg = reply ;
2015-11-20 16:06:18 +03:00
* new_spnego = gensec_have_feature ( auth_generic_state - > gensec_security ,
GENSEC_FEATURE_NEW_SPNEGO ) ;
2006-08-19 05:04:54 +04:00
status = NT_STATUS_OK ;
done :
2011-12-27 07:59:17 +04:00
TALLOC_FREE ( auth_generic_state ) ;
2006-08-19 05:04:54 +04:00
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 ;
2012-03-24 19:00:36 +04:00
gid_t ret_gid ;
2006-08-19 05:04:54 +04:00
ret_uid = ( uid_t ) - 1 ;
2012-03-24 19:00:36 +04:00
ret = getpeereid ( state - > sock , & ret_uid , & ret_gid ) ;
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 ;
}
2013-07-01 15:02:48 +04:00
if ( uid ! = ret_uid & & ret_uid ! = sec_initial_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 ;
}
2018-05-02 21:23:54 +03:00
bool winbindd_ccache_ntlm_auth ( struct winbindd_cli_state * state )
2006-08-19 05:04:54 +04:00
{
struct winbindd_domain * domain ;
2023-10-21 14:06:10 +03:00
fstring f_name_namespace , f_name_domain , f_name_user ;
char * name_namespace = NULL ;
char * name_domain = NULL ;
char * name_user = NULL ;
char * auth_user = NULL ;
2010-04-18 16:14:43 +04:00
NTSTATUS result = NT_STATUS_NOT_SUPPORTED ;
struct WINBINDD_MEMORY_CREDS * entry ;
DATA_BLOB initial , challenge , auth ;
2015-04-24 05:04:23 +03:00
uint32_t initial_blob_len , challenge_blob_len , extra_len ;
2018-04-26 13:17:12 +03:00
bool ok ;
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 */
2023-10-21 14:06:10 +03:00
auth_user = state - > request - > data . ccache_ntlm_auth . user ;
ok = canonicalize_username ( state ,
& auth_user ,
& name_namespace ,
& name_domain ,
& name_user ) ;
2018-04-26 18:32:42 +03:00
if ( ! ok ) {
2006-08-19 05:04:54 +04:00
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 ) ) ;
2018-05-02 21:23:54 +03:00
return false ;
2006-08-19 05:04:54 +04:00
}
2023-10-21 14:06:10 +03:00
fstrcpy ( state - > request - > data . ccache_ntlm_auth . user , auth_user ) ;
TALLOC_FREE ( auth_user ) ;
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 ) ) ;
2018-05-02 21:23:54 +03:00
return false ;
2006-08-19 05:04:54 +04:00
}
2009-05-08 00:46:27 +04:00
if ( ! check_client_uid ( state , state - > request - > data . ccache_ntlm_auth . uid ) ) {
2018-05-02 21:23:54 +03:00
return false ;
2006-08-19 05:04:54 +04:00
}
/* 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 ;
}
2023-10-21 14:06:10 +03:00
TALLOC_FREE ( name_namespace ) ;
TALLOC_FREE ( name_domain ) ;
TALLOC_FREE ( name_user ) ;
2006-08-19 05:04:54 +04:00
/* Parse domain and username */
2023-10-21 23:35:06 +03:00
ok = parse_domain_user_fstr ( state - > request - > data . ccache_ntlm_auth . user ,
2023-10-21 14:06:10 +03:00
f_name_namespace ,
f_name_domain ,
f_name_user ) ;
2018-04-26 13:17:12 +03:00
if ( ! ok ) {
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 ;
}
2023-10-21 14:06:10 +03:00
name_namespace = talloc_strdup ( state , f_name_namespace ) ;
if ( name_namespace = = NULL ) {
result = NT_STATUS_NO_MEMORY ;
goto process_result ;
}
name_domain = talloc_strdup ( state , f_name_domain ) ;
if ( name_domain = = NULL ) {
result = NT_STATUS_NO_MEMORY ;
goto process_result ;
}
name_user = talloc_strdup ( state , f_name_user ) ;
if ( name_user = = NULL ) {
result = NT_STATUS_NO_MEMORY ;
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 ) ;
2011-12-27 07:59:17 +04:00
result = do_ntlm_auth_with_stored_pw (
2018-04-26 13:17:12 +03:00
name_namespace ,
name_domain ,
name_user ,
entry - > pass ,
initial ,
challenge ,
talloc_tos ( ) ,
& auth ,
state - > response - > data . ccache_ntlm_auth . session_key ,
& state - > response - > data . ccache_ntlm_auth . new_spnego ) ;
2010-01-24 18:41:30 +03:00
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 :
2018-05-02 21:23:54 +03:00
return NT_STATUS_IS_OK ( result ) ;
2006-08-19 05:04:54 +04:00
}
2010-01-09 22:20:36 +03:00
2018-05-02 21:26:19 +03:00
bool winbindd_ccache_save ( struct winbindd_cli_state * state )
2010-01-09 22:20:36 +03:00
{
struct winbindd_domain * domain ;
2023-10-21 14:06:10 +03:00
char * name_namespace = NULL ;
char * name_domain = NULL ;
char * name_user = NULL ;
char * save_user = NULL ;
2010-04-18 16:14:43 +04:00
NTSTATUS status ;
2018-04-26 18:32:42 +03:00
bool ok ;
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 */
2023-10-21 14:06:10 +03:00
save_user = state - > request - > data . ccache_save . user ;
ok = canonicalize_username ( state ,
& save_user ,
& name_namespace ,
& name_domain ,
& name_user ) ;
2018-04-26 18:32:42 +03:00
if ( ! ok ) {
2010-01-09 22:20:36 +03:00
DEBUG ( 5 , ( " winbindd_ccache_save: cannot parse domain and user "
" from name [%s] \n " ,
state - > request - > data . ccache_save . user ) ) ;
2018-05-02 21:26:19 +03:00
return false ;
2010-01-09 22:20:36 +03:00
}
2023-10-21 14:06:10 +03:00
fstrcpy ( state - > request - > data . ccache_save . user , save_user ) ;
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 ) ) ;
2018-05-02 21:26:19 +03:00
return false ;
2010-01-09 22:20:36 +03:00
}
if ( ! check_client_uid ( state , state - > request - > data . ccache_save . uid ) ) {
2018-05-02 21:26:19 +03:00
return false ;
2010-01-09 22:20:36 +03:00
}
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 ) ) ) ;
2018-05-02 21:26:19 +03:00
return false ;
2010-01-09 22:20:36 +03:00
}
2018-05-02 21:26:19 +03:00
return true ;
2010-01-09 22:20:36 +03:00
}