2006-02-04 01:19:41 +03:00
/*
Unix SMB / CIFS implementation .
2006-09-23 10:36:22 +04:00
Winbind daemon - krb5 credential cache functions
2006-08-26 06:53:45 +04:00
and in - memory cache functions .
2006-02-04 01:19:41 +03:00
2006-09-08 04:19:32 +04:00
Copyright ( C ) Guenther Deschner 2005 - 2006
2006-08-26 06:53:45 +04:00
Copyright ( C ) Jeremy Allison 2006
2006-02-04 01:19:41 +03: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-02-04 01:19:41 +03: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 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-02-04 01:19:41 +03:00
*/
# include "includes.h"
# include "winbindd.h"
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_WINBIND
2007-04-03 22:32:25 +04:00
/* uncomment this to to fast debugging on the krb5 ticket renewal event */
# ifdef DEBUG_KRB5_TKT_RENEWAL
# undef DEBUG_KRB5_TKT_RENEWAL
# endif
2006-08-26 06:53:45 +04:00
# define MAX_CCACHES 100
2006-02-04 01:19:41 +03:00
static struct WINBINDD_CCACHE_ENTRY * ccache_list ;
2007-04-03 22:32:25 +04:00
/* The Krb5 ticket refresh handler should be scheduled
at one - half of the period from now till the tkt
expiration */
# define KRB5_EVENT_REFRESH_TIME(x) ((x) - (((x) - time(NULL)) / 2))
2006-08-26 06:53:45 +04:00
/****************************************************************
Find an entry by name .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-02-04 01:19:41 +03:00
2006-08-29 03:01:30 +04:00
static struct WINBINDD_CCACHE_ENTRY * get_ccache_by_username ( const char * username )
2006-02-04 01:19:41 +03:00
{
struct WINBINDD_CCACHE_ENTRY * entry ;
for ( entry = ccache_list ; entry ; entry = entry - > next ) {
if ( strequal ( entry - > username , username ) ) {
return entry ;
}
}
return NULL ;
}
2006-08-26 06:53:45 +04:00
/****************************************************************
How many do we have ?
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-02-04 01:19:41 +03:00
static int ccache_entry_count ( void )
{
struct WINBINDD_CCACHE_ENTRY * entry ;
int i = 0 ;
for ( entry = ccache_list ; entry ; entry = entry - > next ) {
i + + ;
}
return i ;
}
2006-08-26 06:53:45 +04:00
/****************************************************************
Do the work of refreshing the ticket .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-02-04 01:19:41 +03:00
2007-01-17 15:59:14 +03:00
static void krb5_ticket_refresh_handler ( struct event_context * event_ctx ,
struct timed_event * te ,
2006-02-04 01:19:41 +03:00
const struct timeval * now ,
void * private_data )
{
struct WINBINDD_CCACHE_ENTRY * entry =
talloc_get_type_abort ( private_data , struct WINBINDD_CCACHE_ENTRY ) ;
2006-09-04 13:29:50 +04:00
# ifdef HAVE_KRB5
2006-02-04 01:19:41 +03:00
int ret ;
time_t new_start ;
2006-08-26 06:53:45 +04:00
struct WINBINDD_MEMORY_CREDS * cred_ptr = entry - > cred_ptr ;
2006-09-04 13:29:50 +04:00
# endif
2006-02-04 01:19:41 +03:00
DEBUG ( 10 , ( " krb5_ticket_refresh_handler called \n " ) ) ;
DEBUGADD ( 10 , ( " event called for: %s, %s \n " , entry - > ccname , entry - > username ) ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( entry - > event ) ;
2006-02-04 01:19:41 +03:00
# ifdef HAVE_KRB5
/* Kinit again if we have the user password and we can't renew the old
* tgt anymore */
2006-08-26 06:53:45 +04:00
if ( ( entry - > renew_until < time ( NULL ) ) & & cred_ptr & & cred_ptr - > pass ) {
2006-02-04 01:19:41 +03:00
2006-05-12 02:47:28 +04:00
set_effective_uid ( entry - > uid ) ;
2006-02-04 01:19:41 +03:00
2006-03-20 22:05:44 +03:00
ret = kerberos_kinit_password_ext ( entry - > principal_name ,
2006-08-26 06:53:45 +04:00
cred_ptr - > pass ,
2006-03-20 22:05:44 +03:00
0 , /* hm, can we do time correction here ? */
& entry - > refresh_time ,
& entry - > renew_until ,
entry - > ccname ,
False , /* no PAC required anymore */
2006-04-25 16:24:25 +04:00
True ,
2007-05-04 14:21:39 +04:00
WINBINDD_PAM_AUTH_KRB5_RENEW_TIME ,
NULL ) ;
2006-05-12 02:47:28 +04:00
gain_root_privilege ( ) ;
2006-02-04 01:19:41 +03:00
if ( ret ) {
2006-09-08 04:19:32 +04:00
DEBUG ( 3 , ( " krb5_ticket_refresh_handler: could not re-kinit: %s \n " ,
error_message ( ret ) ) ) ;
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( entry - > event ) ;
2006-02-04 01:19:41 +03:00
return ;
}
2006-09-08 04:19:32 +04:00
DEBUG ( 10 , ( " krb5_ticket_refresh_handler: successful re-kinit "
" for: %s in ccache: %s \n " ,
2006-02-04 01:19:41 +03:00
entry - > principal_name , entry - > ccname ) ) ;
2007-04-03 22:32:25 +04:00
# if defined(DEBUG_KRB5_TKT_RENEWAL)
new_start = time ( NULL ) + 30 ;
# else
/* The tkt should be refreshed at one-half the period
from now to the expiration time */
new_start = KRB5_EVENT_REFRESH_TIME ( entry - > refresh_time ) ;
# endif
2006-02-04 01:19:41 +03:00
goto done ;
}
2006-05-12 02:47:28 +04:00
set_effective_uid ( entry - > uid ) ;
2006-02-04 01:19:41 +03:00
ret = smb_krb5_renew_ticket ( entry - > ccname ,
entry - > principal_name ,
entry - > service ,
& new_start ) ;
2007-04-03 22:32:25 +04:00
# if defined(DEBUG_KRB5_TKT_RENEWAL)
new_start = time ( NULL ) + 30 ;
# else
new_start = KRB5_EVENT_REFRESH_TIME ( new_start ) ;
# endif
2006-05-12 02:47:28 +04:00
gain_root_privilege ( ) ;
2006-02-04 01:19:41 +03:00
if ( ret ) {
2006-09-08 04:19:32 +04:00
DEBUG ( 3 , ( " krb5_ticket_refresh_handler: could not renew tickets: %s \n " ,
error_message ( ret ) ) ) ;
2006-02-04 01:19:41 +03:00
/* maybe we are beyond the renewing window */
2006-09-08 04:19:32 +04:00
/* avoid breaking the renewal chain: retry in lp_winbind_cache_time()
* seconds when the KDC was not available right now . */
if ( ret = = KRB5_KDC_UNREACH ) {
2006-09-23 10:36:22 +04:00
new_start = time ( NULL ) + MAX ( 30 , lp_winbind_cache_time ( ) ) ;
2006-09-08 04:19:32 +04:00
goto done ;
}
2006-02-04 01:19:41 +03:00
return ;
}
done :
2007-01-17 15:59:14 +03:00
entry - > event = event_add_timed ( winbind_event_context ( ) , entry ,
2006-09-08 04:19:32 +04:00
timeval_set ( new_start , 0 ) ,
2006-02-04 01:19:41 +03:00
" krb5_ticket_refresh_handler " ,
krb5_ticket_refresh_handler ,
entry ) ;
# endif
}
2006-09-08 04:19:32 +04:00
/****************************************************************
Do the work of regaining a ticket when coming from offline auth .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-01-17 15:59:14 +03:00
static void krb5_ticket_gain_handler ( struct event_context * event_ctx ,
struct timed_event * te ,
2006-09-08 04:19:32 +04:00
const struct timeval * now ,
void * private_data )
{
struct WINBINDD_CCACHE_ENTRY * entry =
talloc_get_type_abort ( private_data , struct WINBINDD_CCACHE_ENTRY ) ;
# ifdef HAVE_KRB5
int ret ;
struct timeval t ;
struct WINBINDD_MEMORY_CREDS * cred_ptr = entry - > cred_ptr ;
struct winbindd_domain * domain = NULL ;
# endif
DEBUG ( 10 , ( " krb5_ticket_gain_handler called \n " ) ) ;
DEBUGADD ( 10 , ( " event called for: %s, %s \n " , entry - > ccname , entry - > username ) ) ;
TALLOC_FREE ( entry - > event ) ;
# ifdef HAVE_KRB5
if ( ! cred_ptr | | ! cred_ptr - > pass ) {
DEBUG ( 10 , ( " krb5_ticket_gain_handler: no memory creds \n " ) ) ;
return ;
}
if ( ( domain = find_domain_from_name ( entry - > realm ) ) = = NULL ) {
DEBUG ( 0 , ( " krb5_ticket_gain_handler: unknown domain \n " ) ) ;
return ;
}
if ( domain - > online ) {
set_effective_uid ( entry - > uid ) ;
ret = kerberos_kinit_password_ext ( entry - > principal_name ,
cred_ptr - > pass ,
0 , /* hm, can we do time correction here ? */
& entry - > refresh_time ,
& entry - > renew_until ,
entry - > ccname ,
False , /* no PAC required anymore */
True ,
2007-05-04 14:21:39 +04:00
WINBINDD_PAM_AUTH_KRB5_RENEW_TIME ,
NULL ) ;
2006-09-08 04:19:32 +04:00
gain_root_privilege ( ) ;
if ( ret ) {
DEBUG ( 3 , ( " krb5_ticket_gain_handler: could not kinit: %s \n " ,
error_message ( ret ) ) ) ;
goto retry_later ;
}
DEBUG ( 10 , ( " krb5_ticket_gain_handler: successful kinit for: %s in ccache: %s \n " ,
entry - > principal_name , entry - > ccname ) ) ;
goto got_ticket ;
}
retry_later :
2007-01-17 15:59:14 +03:00
entry - > event = event_add_timed ( winbind_event_context ( ) , entry ,
2006-09-23 10:42:28 +04:00
timeval_current_ofs ( MAX ( 30 , lp_winbind_cache_time ( ) ) , 0 ) ,
2006-09-08 04:19:32 +04:00
" krb5_ticket_gain_handler " ,
krb5_ticket_gain_handler ,
entry ) ;
return ;
got_ticket :
2007-04-03 22:32:25 +04:00
# if defined(DEBUG_KRB5_TKT_RENEWAL)
2006-09-08 04:19:32 +04:00
t = timeval_set ( time ( NULL ) + 30 , 0 ) ;
# else
2007-04-03 22:32:25 +04:00
t = timeval_set ( KRB5_EVENT_REFRESH_TIME ( entry - > refresh_time ) , 0 ) ;
# endif
2006-09-08 04:19:32 +04:00
2007-01-17 15:59:14 +03:00
entry - > event = event_add_timed ( winbind_event_context ( ) , entry ,
2006-09-08 04:19:32 +04:00
t ,
" krb5_ticket_refresh_handler " ,
krb5_ticket_refresh_handler ,
entry ) ;
return ;
# endif
}
2007-02-26 12:53:35 +03:00
/****************************************************************
Check if an ccache entry exists .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL ccache_entry_exists ( const char * username )
{
struct WINBINDD_CCACHE_ENTRY * entry = get_ccache_by_username ( username ) ;
return ( entry ! = NULL ) ;
}
2006-08-26 06:53:45 +04:00
/****************************************************************
Ensure we ' re changing the correct entry .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL ccache_entry_identical ( const char * username , uid_t uid , const char * ccname )
{
struct WINBINDD_CCACHE_ENTRY * entry = get_ccache_by_username ( username ) ;
if ( ! entry ) {
return False ;
}
if ( entry - > uid ! = uid ) {
DEBUG ( 0 , ( " cache_entry_identical: uid's differ: %u != %u \n " ,
( unsigned int ) entry - > uid , ( unsigned int ) uid ) ) ;
return False ;
}
if ( ! strcsequal ( entry - > ccname , ccname ) ) {
DEBUG ( 0 , ( " cache_entry_identical: ccnames differ: (cache) %s != (client) %s \n " ,
entry - > ccname , ccname ) ) ;
return False ;
}
return True ;
}
2006-02-04 01:19:41 +03:00
NTSTATUS add_ccache_to_list ( const char * princ_name ,
const char * ccname ,
const char * service ,
const char * username ,
2006-09-08 04:19:32 +04:00
const char * realm ,
2006-02-04 01:19:41 +03:00
uid_t uid ,
time_t create_time ,
time_t ticket_end ,
time_t renew_until ,
2006-09-08 04:19:32 +04:00
BOOL postponed_request )
2006-02-04 01:19:41 +03:00
{
2006-08-26 06:53:45 +04:00
struct WINBINDD_CCACHE_ENTRY * entry = NULL ;
2006-02-04 01:19:41 +03:00
2006-09-08 04:19:32 +04:00
if ( ( username = = NULL & & princ_name = = NULL ) | | ccname = = NULL | | uid < 0 ) {
2006-02-04 01:19:41 +03:00
return NT_STATUS_INVALID_PARAMETER ;
}
if ( ccache_entry_count ( ) + 1 > MAX_CCACHES ) {
DEBUG ( 10 , ( " add_ccache_to_list: max number of ccaches reached \n " ) ) ;
return NT_STATUS_NO_MORE_ENTRIES ;
}
2006-08-26 06:53:45 +04:00
/* Reference count old entries */
entry = get_ccache_by_username ( username ) ;
if ( entry ) {
/* Check cached entries are identical. */
if ( ! ccache_entry_identical ( username , uid , ccname ) ) {
return NT_STATUS_INVALID_PARAMETER ;
2006-05-02 23:15:14 +04:00
}
2006-08-26 06:53:45 +04:00
entry - > ref_count + + ;
DEBUG ( 10 , ( " add_ccache_to_list: ref count on entry %s is now %d \n " ,
username , entry - > ref_count ) ) ;
2007-02-26 12:53:35 +03:00
/* FIXME: in this case we still might want to have a krb5 cred
* event handler created - gd */
2006-08-26 06:53:45 +04:00
return NT_STATUS_OK ;
2006-05-02 23:15:14 +04:00
}
2006-08-26 06:53:45 +04:00
entry = TALLOC_P ( NULL , struct WINBINDD_CCACHE_ENTRY ) ;
if ( ! entry ) {
2006-02-04 01:19:41 +03:00
return NT_STATUS_NO_MEMORY ;
}
2006-08-26 06:53:45 +04:00
ZERO_STRUCTP ( entry ) ;
2006-02-04 01:19:41 +03:00
if ( username ) {
2006-08-26 06:53:45 +04:00
entry - > username = talloc_strdup ( entry , username ) ;
if ( ! entry - > username ) {
goto no_mem ;
}
2006-02-04 01:19:41 +03:00
}
if ( princ_name ) {
2006-08-26 06:53:45 +04:00
entry - > principal_name = talloc_strdup ( entry , princ_name ) ;
if ( ! entry - > principal_name ) {
goto no_mem ;
}
2006-02-04 01:19:41 +03:00
}
if ( service ) {
2006-08-26 06:53:45 +04:00
entry - > service = talloc_strdup ( entry , service ) ;
if ( ! entry - > service ) {
goto no_mem ;
}
2006-02-04 01:19:41 +03:00
}
2006-05-16 18:29:39 +04:00
2006-08-26 06:53:45 +04:00
entry - > ccname = talloc_strdup ( entry , ccname ) ;
if ( ! entry - > ccname ) {
goto no_mem ;
}
2006-08-20 05:25:26 +04:00
2006-09-08 04:19:32 +04:00
entry - > realm = talloc_strdup ( entry , realm ) ;
if ( ! entry - > realm ) {
goto no_mem ;
}
2006-08-26 06:53:45 +04:00
entry - > create_time = create_time ;
entry - > renew_until = renew_until ;
entry - > uid = uid ;
2006-09-06 08:41:43 +04:00
entry - > ref_count = 1 ;
2006-08-20 05:25:26 +04:00
2007-01-13 13:40:09 +03:00
if ( lp_winbind_refresh_tickets ( ) & & renew_until > 0 ) {
2006-09-08 04:19:32 +04:00
if ( postponed_request ) {
2007-01-17 15:59:14 +03:00
entry - > event = event_add_timed ( winbind_event_context ( ) , entry ,
2006-09-23 10:42:28 +04:00
timeval_current_ofs ( MAX ( 30 , lp_winbind_cache_time ( ) ) , 0 ) ,
2006-09-08 04:19:32 +04:00
" krb5_ticket_gain_handler " ,
krb5_ticket_gain_handler ,
entry ) ;
} else {
2007-03-01 06:15:22 +03:00
/* Renew at 1/2 the ticket expiration time */
2007-01-17 15:59:14 +03:00
entry - > event = event_add_timed ( winbind_event_context ( ) , entry ,
2007-04-03 22:32:25 +04:00
# if defined(DEBUG_KRB5_TKT_RENEWAL)
timeval_set ( time ( NULL ) + 30 , 0 ) ,
# else
timeval_set ( KRB5_EVENT_REFRESH_TIME ( ticket_end ) , 0 ) ,
# endif
2006-09-08 04:19:32 +04:00
" krb5_ticket_refresh_handler " ,
krb5_ticket_refresh_handler ,
entry ) ;
}
2006-08-20 05:25:26 +04:00
2006-09-08 04:19:32 +04:00
if ( ! entry - > event ) {
goto no_mem ;
}
2006-08-20 05:25:26 +04:00
2006-09-08 04:19:32 +04:00
DEBUG ( 10 , ( " add_ccache_to_list: added krb5_ticket handler \n " ) ) ;
2006-08-26 06:53:45 +04:00
}
DLIST_ADD ( ccache_list , entry ) ;
DEBUG ( 10 , ( " add_ccache_to_list: added ccache [%s] for user [%s] to the list \n " , ccname , username ) ) ;
2006-08-20 05:25:26 +04:00
2006-08-26 06:53:45 +04:00
return NT_STATUS_OK ;
no_mem :
TALLOC_FREE ( entry ) ;
return NT_STATUS_NO_MEMORY ;
}
2006-12-14 19:34:24 +03:00
/*******************************************************************
Remove a WINBINDD_CCACHE_ENTRY entry and the krb5 ccache if no longer referenced .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-08-26 06:53:45 +04:00
NTSTATUS remove_ccache ( const char * username )
{
struct WINBINDD_CCACHE_ENTRY * entry = get_ccache_by_username ( username ) ;
2007-08-20 16:29:07 +04:00
NTSTATUS status = NT_STATUS_OK ;
2006-12-14 19:34:24 +03:00
# ifdef HAVE_KRB5
krb5_error_code ret ;
# endif
2006-08-26 06:53:45 +04:00
if ( ! entry ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
if ( entry - > ref_count < = 0 ) {
DEBUG ( 0 , ( " remove_ccache: logic error. ref count for user %s = %d \n " ,
username , entry - > ref_count ) ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
entry - > ref_count - - ;
2006-12-14 19:34:24 +03:00
if ( entry - > ref_count > 0 ) {
2006-08-26 06:53:45 +04:00
DEBUG ( 10 , ( " remove_ccache: entry %s ref count now %d \n " ,
username , entry - > ref_count ) ) ;
2006-12-14 19:34:24 +03:00
return NT_STATUS_OK ;
2006-08-26 06:53:45 +04:00
}
2006-12-14 19:34:24 +03:00
/* no references any more */
DLIST_REMOVE ( ccache_list , entry ) ;
TALLOC_FREE ( entry - > event ) ; /* unregisters events */
# ifdef HAVE_KRB5
ret = ads_kdestroy ( entry - > ccname ) ;
2007-01-05 02:41:16 +03:00
/* we ignore the error when there has been no credential cache */
if ( ret = = KRB5_FCC_NOFILE ) {
ret = 0 ;
} else if ( ret ) {
2006-12-14 19:34:24 +03:00
DEBUG ( 0 , ( " remove_ccache: failed to destroy user krb5 ccache %s with: %s \n " ,
entry - > ccname , error_message ( ret ) ) ) ;
} else {
DEBUG ( 10 , ( " remove_ccache: successfully destroyed krb5 ccache %s for user %s \n " ,
entry - > ccname , username ) ) ;
}
status = krb5_to_nt_status ( ret ) ;
# endif
TALLOC_FREE ( entry ) ;
DEBUG ( 10 , ( " remove_ccache: removed ccache for user %s \n " , username ) ) ;
return status ;
2006-08-26 06:53:45 +04:00
}
/*******************************************************************
In memory credentials cache code .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static struct WINBINDD_MEMORY_CREDS * memory_creds_list ;
/***********************************************************
Find an entry on the list by name .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-08-29 03:01:30 +04:00
struct WINBINDD_MEMORY_CREDS * find_memory_creds_by_name ( const char * username )
2006-08-26 06:53:45 +04:00
{
struct WINBINDD_MEMORY_CREDS * p ;
2006-08-20 05:25:26 +04:00
2006-08-26 06:53:45 +04:00
for ( p = memory_creds_list ; p ; p = p - > next ) {
if ( strequal ( p - > username , username ) ) {
return p ;
2006-08-20 05:25:26 +04:00
}
2006-02-04 01:19:41 +03:00
}
2006-08-26 06:53:45 +04:00
return NULL ;
}
2006-02-04 01:19:41 +03:00
2006-08-26 06:53:45 +04:00
/***********************************************************
Store the required creds and mlock them .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-09-08 04:19:32 +04:00
static NTSTATUS store_memory_creds ( struct WINBINDD_MEMORY_CREDS * memcredp , const char * pass )
2006-08-26 06:53:45 +04:00
{
# if !defined(HAVE_MLOCK)
return NT_STATUS_OK ;
# else
/* new_entry->nt_hash is the base pointer for the block
of memory pointed into by new_entry - > lm_hash and
new_entry - > pass ( if we ' re storing plaintext ) . */
memcredp - > len = NT_HASH_LEN + LM_HASH_LEN ;
2006-09-08 04:19:32 +04:00
if ( pass ) {
2006-08-26 06:53:45 +04:00
memcredp - > len + = strlen ( pass ) + 1 ;
2006-02-04 01:19:41 +03:00
}
2007-04-03 22:32:25 +04:00
2007-03-01 06:14:20 +03:00
# if defined(LINUX)
/* aligning the memory on on x86_64 and compiling
with gcc 4.1 using - O2 causes a segv in the
next memset ( ) - - jerry */
memcredp - > nt_hash = SMB_MALLOC_ARRAY ( unsigned char , memcredp - > len ) ;
# else
/* On non-linux platforms, mlock()'d memory must be aligned */
memcredp - > nt_hash = SMB_MEMALIGN_ARRAY ( unsigned char ,
getpagesize ( ) , memcredp - > len ) ;
# endif
2007-02-24 15:40:43 +03:00
if ( ! memcredp - > nt_hash ) {
2006-08-26 06:53:45 +04:00
return NT_STATUS_NO_MEMORY ;
}
2007-02-24 15:40:43 +03:00
memset ( memcredp - > nt_hash , 0x0 , memcredp - > len ) ;
2006-02-04 01:19:41 +03:00
2006-08-26 06:53:45 +04:00
memcredp - > lm_hash = memcredp - > nt_hash + NT_HASH_LEN ;
2007-02-22 20:21:27 +03:00
2006-08-26 06:53:45 +04:00
# ifdef DEBUG_PASSWORD
DEBUG ( 10 , ( " mlocking memory: %p \n " , memcredp - > nt_hash ) ) ;
# endif
2007-02-24 15:40:43 +03:00
if ( ( mlock ( memcredp - > nt_hash , memcredp - > len ) ) = = - 1 ) {
2006-08-26 06:53:45 +04:00
DEBUG ( 0 , ( " failed to mlock memory: %s (%d) \n " ,
strerror ( errno ) , errno ) ) ;
2007-03-01 08:52:38 +03:00
SAFE_FREE ( memcredp - > nt_hash ) ;
2006-08-26 06:53:45 +04:00
return map_nt_error_from_unix ( errno ) ;
2006-02-04 01:19:41 +03:00
}
2006-08-26 06:53:45 +04:00
# ifdef DEBUG_PASSWORD
DEBUG ( 10 , ( " mlocked memory: %p \n " , memcredp - > nt_hash ) ) ;
# endif
2006-02-04 01:19:41 +03:00
2006-08-26 06:53:45 +04:00
/* Create and store the password hashes. */
E_md4hash ( pass , memcredp - > nt_hash ) ;
E_deshash ( pass , memcredp - > lm_hash ) ;
2006-09-08 04:19:32 +04:00
if ( pass ) {
2006-08-26 06:53:45 +04:00
memcredp - > pass = ( char * ) memcredp - > lm_hash + LM_HASH_LEN ;
memcpy ( memcredp - > pass , pass , memcredp - > len - NT_HASH_LEN - LM_HASH_LEN ) ;
}
2006-02-04 01:19:41 +03:00
return NT_STATUS_OK ;
2006-08-26 06:53:45 +04:00
# endif
2006-02-04 01:19:41 +03:00
}
2006-08-26 06:53:45 +04:00
/***********************************************************
Destroy existing creds .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS delete_memory_creds ( struct WINBINDD_MEMORY_CREDS * memcredp )
2006-02-04 01:19:41 +03:00
{
2006-08-26 06:53:45 +04:00
# if !defined(HAVE_MUNLOCK)
return NT_STATUS_OK ;
# else
2007-02-24 15:40:43 +03:00
if ( munlock ( memcredp - > nt_hash , memcredp - > len ) = = - 1 ) {
2006-08-26 06:53:45 +04:00
DEBUG ( 0 , ( " failed to munlock memory: %s (%d) \n " ,
2006-05-16 18:29:39 +04:00
strerror ( errno ) , errno ) ) ;
return map_nt_error_from_unix ( errno ) ;
}
2007-02-24 15:40:43 +03:00
memset ( memcredp - > nt_hash , ' \0 ' , memcredp - > len ) ;
SAFE_FREE ( memcredp - > nt_hash ) ;
2007-02-22 20:21:27 +03:00
memcredp - > nt_hash = NULL ;
2006-08-26 06:53:45 +04:00
memcredp - > lm_hash = NULL ;
memcredp - > pass = NULL ;
memcredp - > len = 0 ;
return NT_STATUS_OK ;
# endif
2006-02-04 01:19:41 +03:00
}
2006-08-26 06:53:45 +04:00
/***********************************************************
Replace the required creds with new ones ( password change ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS winbindd_replace_memory_creds_internal ( struct WINBINDD_MEMORY_CREDS * memcredp ,
2006-09-08 04:19:32 +04:00
const char * pass )
2006-02-04 01:19:41 +03:00
{
2006-08-26 06:53:45 +04:00
NTSTATUS status = delete_memory_creds ( memcredp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-09-08 04:19:32 +04:00
return store_memory_creds ( memcredp , pass ) ;
2006-08-26 06:53:45 +04:00
}
/*************************************************************
Store credentials in memory in a list .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-09-08 04:19:32 +04:00
static NTSTATUS winbindd_add_memory_creds_internal ( const char * username , uid_t uid , const char * pass )
2006-08-26 06:53:45 +04:00
{
/* Shortcut to ensure we don't store if no mlock. */
# if !defined(HAVE_MLOCK) || !defined(HAVE_MUNLOCK)
return NT_STATUS_OK ;
# else
NTSTATUS status ;
struct WINBINDD_MEMORY_CREDS * memcredp = find_memory_creds_by_name ( username ) ;
2006-08-29 03:01:30 +04:00
if ( uid = = ( uid_t ) - 1 ) {
DEBUG ( 0 , ( " winbindd_add_memory_creds_internal: invalid uid for user %s. \n " ,
username ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2006-08-26 06:53:45 +04:00
if ( memcredp ) {
/* Already exists. Increment the reference count and replace stored creds. */
2006-08-29 03:01:30 +04:00
if ( uid ! = memcredp - > uid ) {
DEBUG ( 0 , ( " winbindd_add_memory_creds_internal: uid %u for user %s doesn't "
" match stored uid %u. Replacing. \n " ,
( unsigned int ) uid , username , ( unsigned int ) memcredp - > uid ) ) ;
memcredp - > uid = uid ;
}
2006-08-26 06:53:45 +04:00
memcredp - > ref_count + + ;
DEBUG ( 10 , ( " winbindd_add_memory_creds_internal: ref count for user %s is now %d \n " ,
username , memcredp - > ref_count ) ) ;
2006-09-08 04:19:32 +04:00
return winbindd_replace_memory_creds_internal ( memcredp , pass ) ;
2006-02-04 01:19:41 +03:00
}
2006-08-26 06:53:45 +04:00
memcredp = TALLOC_ZERO_P ( NULL , struct WINBINDD_MEMORY_CREDS ) ;
if ( ! memcredp ) {
return NT_STATUS_NO_MEMORY ;
}
memcredp - > username = talloc_strdup ( memcredp , username ) ;
if ( ! memcredp - > username ) {
talloc_destroy ( memcredp ) ;
2006-02-04 01:19:41 +03:00
return NT_STATUS_NO_MEMORY ;
}
2006-09-08 04:19:32 +04:00
status = store_memory_creds ( memcredp , pass ) ;
2006-08-26 06:53:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2006-08-29 03:01:30 +04:00
talloc_destroy ( memcredp ) ;
2006-08-26 06:53:45 +04:00
return status ;
}
2006-08-29 03:01:30 +04:00
memcredp - > uid = uid ;
2006-08-26 06:53:45 +04:00
memcredp - > ref_count = 1 ;
DLIST_ADD ( memory_creds_list , memcredp ) ;
DEBUG ( 10 , ( " winbindd_add_memory_creds_internal: added entry for user %s \n " ,
username ) ) ;
2006-02-04 01:19:41 +03:00
return NT_STATUS_OK ;
2006-08-26 06:53:45 +04:00
# endif
}
/*************************************************************
Store users credentials in memory . If we also have a
struct WINBINDD_CCACHE_ENTRY for this username with a
refresh timer , then store the plaintext of the password
and associate the new credentials with the struct WINBINDD_CCACHE_ENTRY .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-08-29 03:01:30 +04:00
NTSTATUS winbindd_add_memory_creds ( const char * username , uid_t uid , const char * pass )
2006-08-26 06:53:45 +04:00
{
struct WINBINDD_CCACHE_ENTRY * entry = get_ccache_by_username ( username ) ;
NTSTATUS status ;
2006-09-08 04:19:32 +04:00
status = winbindd_add_memory_creds_internal ( username , uid , pass ) ;
2006-08-26 06:53:45 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2006-09-08 04:19:32 +04:00
if ( entry ) {
2006-08-26 06:53:45 +04:00
struct WINBINDD_MEMORY_CREDS * memcredp = find_memory_creds_by_name ( username ) ;
if ( memcredp ) {
entry - > cred_ptr = memcredp ;
}
}
return status ;
}
/*************************************************************
Decrement the in - memory ref count - delete if zero .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS winbindd_delete_memory_creds ( const char * username )
{
struct WINBINDD_MEMORY_CREDS * memcredp = find_memory_creds_by_name ( username ) ;
2006-09-08 04:19:32 +04:00
struct WINBINDD_CCACHE_ENTRY * entry = get_ccache_by_username ( username ) ;
2006-08-26 06:53:45 +04:00
NTSTATUS status = NT_STATUS_OK ;
if ( ! memcredp ) {
DEBUG ( 10 , ( " winbindd_delete_memory_creds: unknown user %s \n " ,
username ) ) ;
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
if ( memcredp - > ref_count < = 0 ) {
DEBUG ( 0 , ( " winbindd_delete_memory_creds: logic error. ref count for user %s = %d \n " ,
username , memcredp - > ref_count ) ) ;
status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
memcredp - > ref_count - - ;
if ( memcredp - > ref_count < = 0 ) {
delete_memory_creds ( memcredp ) ;
DLIST_REMOVE ( memory_creds_list , memcredp ) ;
talloc_destroy ( memcredp ) ;
DEBUG ( 10 , ( " winbindd_delete_memory_creds: deleted entry for user %s \n " ,
username ) ) ;
} else {
DEBUG ( 10 , ( " winbindd_delete_memory_creds: entry for user %s ref_count now %d \n " ,
username , memcredp - > ref_count ) ) ;
}
2006-09-08 04:19:32 +04:00
if ( entry ) {
entry - > cred_ptr = NULL ; /* Ensure we have no dangling references to this. */
}
2006-08-26 06:53:45 +04:00
return status ;
}
/***********************************************************
Replace the required creds with new ones ( password change ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
NTSTATUS winbindd_replace_memory_creds ( const char * username , const char * pass )
{
struct WINBINDD_MEMORY_CREDS * memcredp = find_memory_creds_by_name ( username ) ;
if ( ! memcredp ) {
DEBUG ( 10 , ( " winbindd_replace_memory_creds: unknown user %s \n " ,
username ) ) ;
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
DEBUG ( 10 , ( " winbindd_replace_memory_creds: replaced creds for user %s \n " ,
username ) ) ;
2006-09-08 04:19:32 +04:00
return winbindd_replace_memory_creds_internal ( memcredp , pass ) ;
2006-02-04 01:19:41 +03:00
}