2000-05-09 15:43:00 +04:00
/*
Unix SMB / Netbios implementation .
Version 2.0
Winbind daemon - caching related functions
Copyright ( C ) Tim Potter 2000
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 "winbindd.h"
# define CACHE_TYPE_USER "USR"
# define CACHE_TYPE_GROUP "GRP"
/* Initialise caching system */
static TDB_CONTEXT * cache_tdb ;
struct cache_rec {
uint32 seq_num ;
time_t mod_time ;
} ;
void winbindd_cache_init ( void )
{
/* Open tdb cache */
unlink ( lock_path ( " winbindd_cache.tdb " ) ) ;
if ( ! ( cache_tdb = tdb_open ( lock_path ( " winbindd_cache.tdb " ) , 0 ,
TDB_NOLOCK ,
O_RDWR | O_CREAT , 0600 ) ) ) {
DEBUG ( 0 , ( " Unable to open tdb cache - user and group caching "
" disabled \n " ) ) ;
}
}
/* get the domain sequence number, possibly re-fetching */
static uint32 cached_sequence_number ( char * domain_name )
{
fstring keystr ;
TDB_DATA dbuf ;
struct cache_rec rec ;
time_t t = time ( NULL ) ;
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " CACHESEQ/%s " , domain_name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
dbuf = tdb_fetch_by_string ( cache_tdb , keystr ) ;
if ( ! dbuf . dptr | | dbuf . dsize ! = sizeof ( rec ) ) {
goto refetch ;
}
memcpy ( & rec , dbuf . dptr , sizeof ( rec ) ) ;
free ( dbuf . dptr ) ;
if ( t < ( rec . mod_time + lp_winbind_cache_time ( ) ) ) {
DEBUG ( 4 , ( " cached sequence number for %s is %u \n " ,
domain_name , ( unsigned ) rec . seq_num ) ) ;
return rec . seq_num ;
}
refetch :
rec . seq_num = domain_sequence_number ( domain_name ) ;
rec . mod_time = t ;
tdb_store_by_string ( cache_tdb , keystr , & rec , sizeof ( rec ) ) ;
return rec . seq_num ;
}
/* Check whether a seq_num for a cached item has expired */
static BOOL cache_domain_expired ( char * domain_name , uint32 seq_num )
{
if ( cached_sequence_number ( domain_name ) ! = seq_num ) {
DEBUG ( 4 , ( " seq %u for %s has expired \n " , ( unsigned ) seq_num , domain_name ) ) ;
return True ;
}
return False ;
}
static void set_cache_sequence_number ( char * domain_name , char * cache_type , char * subkey )
{
fstring keystr ;
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " CACHESEQ %s/%s/%s " ,
2000-05-09 15:43:00 +04:00
domain_name , cache_type , subkey ? subkey : " " ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
tdb_store_int ( cache_tdb , keystr , cached_sequence_number ( domain_name ) ) ;
}
static uint32 get_cache_sequence_number ( char * domain_name , char * cache_type , char * subkey )
{
fstring keystr ;
uint32 seq_num ;
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " CACHESEQ %s/%s/%s " ,
2000-05-09 15:43:00 +04:00
domain_name , cache_type , subkey ? subkey : " " ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-12 10:27:35 +04:00
seq_num = ( uint32 ) tdb_fetch_int ( cache_tdb , keystr ) ;
2000-05-09 15:43:00 +04:00
DEBUG ( 4 , ( " %s is %u \n " , keystr , ( unsigned ) seq_num ) ) ;
return seq_num ;
}
/* Fill the user or group cache with supplied data */
static void fill_cache ( char * domain_name , char * cache_type ,
struct acct_info * sam_entries ,
int num_sam_entries )
{
fstring keystr ;
if ( lp_winbind_cache_time ( ) = = 0 ) return ;
/* Error check */
if ( ! sam_entries | | ( num_sam_entries = = 0 ) ) return ;
DEBUG ( 4 , ( " filling %s cache for domain %s with %d entries \n " ,
cache_type , domain_name , num_sam_entries ) ) ;
/* Store data as a mega-huge chunk in the tdb */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s CACHE DATA/%s " , cache_type ,
2000-05-09 15:43:00 +04:00
domain_name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
tdb_store_by_string ( cache_tdb , keystr ,
sam_entries , sizeof ( struct acct_info ) * num_sam_entries ) ;
/* Stamp cache with current seq number */
set_cache_sequence_number ( domain_name , cache_type , NULL ) ;
}
/* Fill the user cache with supplied data */
void winbindd_fill_user_cache ( char * domain_name ,
struct acct_info * sam_entries ,
int num_sam_entries )
{
fill_cache ( domain_name , CACHE_TYPE_USER , sam_entries , num_sam_entries ) ;
}
/* Fill the group cache with supplied data */
void winbindd_fill_group_cache ( char * domain_name ,
struct acct_info * sam_entries ,
int num_sam_entries )
{
fill_cache ( domain_name , CACHE_TYPE_GROUP , sam_entries , num_sam_entries ) ;
}
2000-05-09 19:08:46 +04:00
static void fill_cache_entry ( char * domain , char * cache_type , char * name , void * buf , int len )
2000-05-09 15:43:00 +04:00
{
fstring keystr ;
/* Create key for store */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s " , cache_type , domain , name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
DEBUG ( 4 , ( " filling cache entry %s \n " , keystr ) ) ;
/* Store it */
tdb_store_by_string ( cache_tdb , keystr , buf , len ) ;
}
/* Fill a user info cache entry */
void winbindd_fill_user_cache_entry ( char * domain , char * user_name ,
struct winbindd_pw * pw )
{
if ( lp_winbind_cache_time ( ) = = 0 ) return ;
2000-05-09 19:08:46 +04:00
fill_cache_entry ( domain , CACHE_TYPE_USER , user_name , pw , sizeof ( struct winbindd_pw ) ) ;
2000-05-09 15:43:00 +04:00
set_cache_sequence_number ( domain , CACHE_TYPE_USER , user_name ) ;
}
/* Fill a user uid cache entry */
void winbindd_fill_uid_cache_entry ( char * domain , uid_t uid ,
struct winbindd_pw * pw )
{
fstring uidstr ;
if ( lp_winbind_cache_time ( ) = = 0 ) return ;
2001-04-09 00:22:39 +04:00
slprintf ( uidstr , sizeof ( uidstr ) - 1 , " #%u " , ( unsigned ) uid ) ;
2000-05-09 19:08:46 +04:00
fill_cache_entry ( domain , CACHE_TYPE_USER , uidstr , pw , sizeof ( struct winbindd_pw ) ) ;
2000-05-09 15:43:00 +04:00
set_cache_sequence_number ( domain , CACHE_TYPE_USER , uidstr ) ;
}
/* Fill a group info cache entry */
void winbindd_fill_group_cache_entry ( char * domain , char * group_name ,
struct winbindd_gr * gr , void * extra_data ,
int extra_data_len )
{
fstring keystr ;
if ( lp_winbind_cache_time ( ) = = 0 ) return ;
/* Fill group data */
2000-05-09 19:08:46 +04:00
fill_cache_entry ( domain , CACHE_TYPE_GROUP , group_name , gr , sizeof ( struct winbindd_gr ) ) ;
2000-05-09 15:43:00 +04:00
/* Fill extra data */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s DATA " , CACHE_TYPE_GROUP , domain , group_name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
tdb_store_by_string ( cache_tdb , keystr , extra_data , extra_data_len ) ;
set_cache_sequence_number ( domain , CACHE_TYPE_GROUP , group_name ) ;
}
/* Fill a group info cache entry */
void winbindd_fill_gid_cache_entry ( char * domain , gid_t gid ,
struct winbindd_gr * gr , void * extra_data ,
int extra_data_len )
{
fstring keystr ;
fstring gidstr ;
2001-04-09 00:22:39 +04:00
slprintf ( gidstr , sizeof ( gidstr ) - 1 , " #%u " , ( unsigned ) gid ) ;
2000-05-09 15:43:00 +04:00
if ( lp_winbind_cache_time ( ) = = 0 ) return ;
/* Fill group data */
2000-05-09 19:08:46 +04:00
fill_cache_entry ( domain , CACHE_TYPE_GROUP , gidstr , gr , sizeof ( struct winbindd_gr ) ) ;
2000-05-09 15:43:00 +04:00
/* Fill extra data */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s DATA " , CACHE_TYPE_GROUP , domain , gidstr ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
tdb_store_by_string ( cache_tdb , keystr , extra_data , extra_data_len ) ;
set_cache_sequence_number ( domain , CACHE_TYPE_GROUP , gidstr ) ;
}
/* Fetch some cached user or group data */
static BOOL fetch_cache ( char * domain_name , char * cache_type ,
struct acct_info * * sam_entries , int * num_sam_entries )
{
TDB_DATA data ;
fstring keystr ;
if ( lp_winbind_cache_time ( ) = = 0 ) return False ;
/* Parameter check */
if ( ! sam_entries | | ! num_sam_entries ) {
return False ;
}
/* Check cache data is current */
if ( cache_domain_expired ( domain_name ,
get_cache_sequence_number ( domain_name , cache_type , NULL ) ) ) {
return False ;
}
/* Create key */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s CACHE DATA/%s " , cache_type ,
2000-05-09 15:43:00 +04:00
domain_name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
/* Fetch cache information */
data = tdb_fetch_by_string ( cache_tdb , keystr ) ;
if ( ! data . dptr ) return False ;
/* Copy across cached data. We can save a memcpy() by directly
assigning the data . dptr to the sam_entries pointer . It will
be freed by the end { pw , gr } ent ( ) function . */
* sam_entries = ( struct acct_info * ) data . dptr ;
* num_sam_entries = data . dsize / sizeof ( struct acct_info ) ;
DEBUG ( 4 , ( " fetched %d cached %s entries for domain %s \n " ,
* num_sam_entries , cache_type , domain_name ) ) ;
return True ;
}
/* Return cached entries for a domain. Return false if there are no cached
entries , or the cached information has expired for the domain . */
BOOL winbindd_fetch_user_cache ( char * domain_name ,
struct acct_info * * sam_entries ,
int * num_entries )
{
return fetch_cache ( domain_name , CACHE_TYPE_USER , sam_entries ,
num_entries ) ;
}
/* Return cached entries for a domain. Return false if there are no cached
entries , or the cached information has expired for the domain . */
BOOL winbindd_fetch_group_cache ( char * domain_name ,
struct acct_info * * sam_entries ,
int * num_entries )
{
return fetch_cache ( domain_name , CACHE_TYPE_GROUP , sam_entries ,
num_entries ) ;
}
2000-05-09 19:08:46 +04:00
static BOOL fetch_cache_entry ( char * domain , char * cache_type , char * name , void * buf , int len )
2000-05-09 15:43:00 +04:00
{
TDB_DATA data ;
fstring keystr ;
/* Create key for lookup */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s " , cache_type , domain , name ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
/* Look up cache entry */
data = tdb_fetch_by_string ( cache_tdb , keystr ) ;
if ( ! data . dptr ) return False ;
2000-05-12 10:27:34 +04:00
DEBUG ( 4 , ( " returning cached entry for %s \\ %s \n " , domain , name ) ) ;
2000-05-09 15:43:00 +04:00
/* Copy found entry into buffer */
memcpy ( ( char * ) buf , data . dptr , len < data . dsize ? len : data . dsize ) ;
free ( data . dptr ) ;
return True ;
}
/* Fetch an individual user cache entry */
BOOL winbindd_fetch_user_cache_entry ( char * domain_name , char * user ,
struct winbindd_pw * pw )
{
uint32 seq_num ;
if ( lp_winbind_cache_time ( ) = = 0 ) return False ;
seq_num = get_cache_sequence_number ( domain_name , CACHE_TYPE_USER , user ) ;
if ( cache_domain_expired ( domain_name , seq_num ) ) return False ;
2000-05-09 19:08:46 +04:00
return fetch_cache_entry ( domain_name , CACHE_TYPE_USER , user , pw , sizeof ( struct winbindd_pw ) ) ;
2000-05-09 15:43:00 +04:00
}
/* Fetch an individual uid cache entry */
BOOL winbindd_fetch_uid_cache_entry ( char * domain_name , uid_t uid ,
struct winbindd_pw * pw )
{
fstring uidstr ;
uint32 seq_num ;
if ( lp_winbind_cache_time ( ) = = 0 ) return False ;
2001-04-09 00:22:39 +04:00
slprintf ( uidstr , sizeof ( uidstr ) - 1 , " #%u " , ( unsigned ) uid ) ;
2000-05-09 15:43:00 +04:00
seq_num = get_cache_sequence_number ( domain_name , CACHE_TYPE_USER , uidstr ) ;
if ( cache_domain_expired ( domain_name , seq_num ) ) return False ;
2000-05-09 19:08:46 +04:00
return fetch_cache_entry ( domain_name , CACHE_TYPE_USER , uidstr , pw , sizeof ( struct winbindd_pw ) ) ;
2000-05-09 15:43:00 +04:00
}
/* Fetch an individual group cache entry. This function differs from the
user cache code as we need to store the group membership data . */
BOOL winbindd_fetch_group_cache_entry ( char * domain_name , char * group ,
struct winbindd_gr * gr ,
void * * extra_data , int * extra_data_len )
{
TDB_DATA data ;
fstring keystr ;
uint32 seq_num ;
if ( lp_winbind_cache_time ( ) = = 0 ) return False ;
seq_num = get_cache_sequence_number ( domain_name , CACHE_TYPE_GROUP , group ) ;
if ( cache_domain_expired ( domain_name , seq_num ) ) return False ;
/* Fetch group data */
2000-05-09 19:08:46 +04:00
if ( ! fetch_cache_entry ( domain_name , CACHE_TYPE_GROUP , group , gr , sizeof ( struct winbindd_gr ) ) ) return False ;
2000-05-09 15:43:00 +04:00
/* Fetch extra data */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s DATA " , CACHE_TYPE_GROUP , domain_name , group ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
data = tdb_fetch_by_string ( cache_tdb , keystr ) ;
if ( ! data . dptr ) return False ;
/* Extra data freed when data has been sent */
if ( extra_data ) * extra_data = data . dptr ;
if ( extra_data_len ) * extra_data_len = data . dsize ;
return True ;
}
/* Fetch an individual gid cache entry. This function differs from the
user cache code as we need to store the group membership data . */
BOOL winbindd_fetch_gid_cache_entry ( char * domain_name , gid_t gid ,
struct winbindd_gr * gr ,
void * * extra_data , int * extra_data_len )
{
TDB_DATA data ;
fstring keystr ;
fstring gidstr ;
uint32 seq_num ;
2001-04-09 00:22:39 +04:00
slprintf ( gidstr , sizeof ( gidstr ) - 1 , " #%u " , ( unsigned ) gid ) ;
2000-05-09 15:43:00 +04:00
if ( lp_winbind_cache_time ( ) = = 0 ) return False ;
seq_num = get_cache_sequence_number ( domain_name , CACHE_TYPE_GROUP , gidstr ) ;
if ( cache_domain_expired ( domain_name , seq_num ) ) return False ;
/* Fetch group data */
2000-05-09 19:08:46 +04:00
if ( ! fetch_cache_entry ( domain_name , CACHE_TYPE_GROUP ,
gidstr , gr , sizeof ( struct winbindd_gr ) ) ) return False ;
2000-05-09 15:43:00 +04:00
/* Fetch extra data */
2001-04-09 00:22:39 +04:00
slprintf ( keystr , sizeof ( keystr ) - 1 , " %s/%s/%s DATA " , CACHE_TYPE_GROUP , domain_name , gidstr ) ;
2001-01-11 23:41:19 +03:00
dos_to_unix ( keystr , True ) ; /* Convert key to unix-codepage */
2000-05-09 15:43:00 +04:00
data = tdb_fetch_by_string ( cache_tdb , keystr ) ;
if ( ! data . dptr ) return False ;
/* Extra data freed when data has been sent */
if ( extra_data ) * extra_data = data . dptr ;
if ( extra_data_len ) * extra_data_len = data . dsize ;
return True ;
}
/* Flush cache data - easiest to just reopen the tdb */
void winbindd_flush_cache ( void )
{
tdb_close ( cache_tdb ) ;
winbindd_cache_init ( ) ;
}