diff options
Diffstat (limited to 'source3/nsswitch/winbindd_cache.c')
-rw-r--r-- | source3/nsswitch/winbindd_cache.c | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c new file mode 100644 index 0000000000..adfcadf099 --- /dev/null +++ b/source3/nsswitch/winbindd_cache.c @@ -0,0 +1,416 @@ +/* + 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 02139, 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); + + slprintf(keystr, sizeof(keystr), "CACHESEQ/%s", domain_name); + 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; + slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s", + domain_name, cache_type, subkey?subkey:""); + 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; + slprintf(keystr,sizeof(keystr),"CACHESEQ %s/%s/%s", + domain_name, cache_type, subkey?subkey:""); + seq_num = (uint32)tdb_get_int(cache_tdb, keystr); + 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 */ + slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, + domain_name); + + 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); +} + +static void fill_cache_entry(char *domain, char *name, void *buf, int len) +{ + fstring keystr; + + /* Create key for store */ + slprintf(keystr, sizeof(keystr), "%s/%s", domain, name); + + 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; + + fill_cache_entry(domain, user_name, pw, sizeof(struct winbindd_pw)); + 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; + + slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + fill_cache_entry(domain, uidstr, pw, sizeof(struct winbindd_pw)); + 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 */ + fill_cache_entry(domain, group_name, gr, sizeof(struct winbindd_gr)); + + /* Fill extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, group_name); + 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; + + slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); + + if (lp_winbind_cache_time() == 0) return; + + /* Fill group data */ + fill_cache_entry(domain, gidstr, gr, sizeof(struct winbindd_gr)); + + /* Fill extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain, gidstr); + 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 */ + slprintf(keystr, sizeof(keystr), "%s CACHE DATA/%s", cache_type, + domain_name); + + /* 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); +} + +static BOOL fetch_cache_entry(char *domain, char *name, void *buf, int len) +{ + TDB_DATA data; + fstring keystr; + + /* Create key for lookup */ + slprintf(keystr, sizeof(keystr), "%s/%s", domain, name); + + /* Look up cache entry */ + data = tdb_fetch_by_string(cache_tdb, keystr); + if (!data.dptr) return False; + + DEBUG(4, ("returning cached entry for %s/%s\n", domain, name)); + + /* 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; + + return fetch_cache_entry(domain_name, user, pw, sizeof(struct winbindd_pw)); +} + +/* 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; + + slprintf(uidstr, sizeof(uidstr), "#%u", (unsigned)uid); + seq_num = get_cache_sequence_number(domain_name, CACHE_TYPE_USER, uidstr); + if (cache_domain_expired(domain_name, seq_num)) return False; + + return fetch_cache_entry(domain_name, uidstr, pw, sizeof(struct winbindd_pw)); +} + +/* 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 */ + if (!fetch_cache_entry(domain_name, group, gr, sizeof(struct winbindd_gr))) return False; + + /* Fetch extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, group); + 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; + + slprintf(gidstr, sizeof(gidstr), "#%u", (unsigned)gid); + + 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 */ + if (!fetch_cache_entry(domain_name, gidstr, gr, sizeof(struct winbindd_gr))) return False; + + /* Fetch extra data */ + slprintf(keystr, sizeof(keystr), "%s/%s DATA", domain_name, gidstr); + 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(); +} |