Winbind cache backend functions
Copyright (C) Andrew Tridgell 2001
+ Copyright (C) Gerald Carter 2003
+
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
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "includes.h"
#include "winbindd.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
struct winbind_cache {
- struct winbindd_methods *backend;
TDB_CONTEXT *tdb;
};
/* get the winbind_cache structure */
static struct winbind_cache *get_cache(struct winbindd_domain *domain)
{
- extern struct winbindd_methods msrpc_methods;
struct winbind_cache *ret = wcache;
+ if (!domain->backend) {
+ extern struct winbindd_methods msrpc_methods;
+ switch (lp_security()) {
+#ifdef HAVE_ADS
+ case SEC_ADS: {
+ extern struct winbindd_methods ads_methods;
+ /* always obey the lp_security parameter for our domain */
+ if (domain->primary) {
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* only use ADS for native modes at the momment.
+ The problem is the correct detection of mixed
+ mode domains from NT4 BDC's --jerry */
+
+ if ( domain->native_mode ) {
+ DEBUG(5,("get_cache: Setting ADS methods for domain %s\n",
+ domain->name));
+ domain->backend = &ads_methods;
+ break;
+ }
+
+ /* fall through */
+ }
+#endif
+ default:
+ DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n",
+ domain->name));
+ domain->backend = &msrpc_methods;
+ }
+ }
+
if (ret)
return ret;
ret = smb_xmalloc(sizeof(*ret));
ZERO_STRUCTP(ret);
- switch (lp_security()) { /* winbind pdc disabled until ready
- if (!strcmp(domain->name, lp_workgroup()) && (lp_security() == SEC_USER)) {
- extern struct winbindd_methods passdb_methods;
- ret->backend = &passdb_methods;
-
- } else switch (lp_security()) { */
-#ifdef HAVE_ADS
- case SEC_ADS: {
- extern struct winbindd_methods ads_methods;
- ret->backend = &ads_methods;
- break;
- }
-#endif
- default:
- ret->backend = &msrpc_methods;
- }
wcache = ret;
wcache_flush_cache();
return NT_STATUS_UNSUCCESSFUL;
}
- snprintf( key, sizeof(key), "SEQNUM/%s", domain->name );
+ fstr_sprintf( key, "SEQNUM/%s", domain->name );
- data = tdb_fetch_by_string( wcache->tdb, key );
+ data = tdb_fetch_bystring( wcache->tdb, key );
if ( !data.dptr || data.dsize!=8 ) {
DEBUG(10,("fetch_cache_seqnum: invalid data size key [%s]\n", key ));
return NT_STATUS_UNSUCCESSFUL;
return NT_STATUS_UNSUCCESSFUL;
}
- snprintf( key_str, sizeof(key_str), "SEQNUM/%s", domain->name );
+ fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
key.dptr = key_str;
key.dsize = strlen(key_str)+1;
time_t t = time(NULL);
unsigned cache_time = lp_winbind_cache_time();
+ get_cache( domain );
+
+#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
/* trying to reconnect is expensive, don't do it too often */
if (domain->sequence_number == DOM_SEQUENCE_NONE) {
cache_time *= 8;
}
+#endif
time_diff = t - domain->last_seq_check;
/* see if we have to refetch the domain sequence number */
if (!force && (time_diff < cache_time)) {
DEBUG(10, ("refresh_sequence_number: %s time ok\n", domain->name));
- return;
+ goto done;
}
/* try to get the sequence number from the tdb cache first */
if ( NT_STATUS_IS_OK(status) )
goto done;
- status = wcache->backend->sequence_number(domain, &domain->sequence_number);
+ status = domain->backend->sequence_number(domain, &domain->sequence_number);
if (!NT_STATUS_IS_OK(status)) {
domain->sequence_number = DOM_SEQUENCE_NONE;
}
+ domain->last_status = status;
domain->last_seq_check = time(NULL);
/* save the new sequence number ni the cache */
}
centry = smb_xmalloc(sizeof(*centry));
- centry->data = data.dptr;
+ centry->data = (unsigned char *)data.dptr;
centry->len = data.dsize;
centry->ofs = 0;
key.dptr = kstr;
key.dsize = strlen(kstr);
- data.dptr = centry->data;
+ data.dptr = (char *)centry->data;
data.dsize = centry->ofs;
tdb_store(wcache->tdb, key, data, TDB_REPLACE);
static void wcache_save_name_to_sid(struct winbindd_domain *domain,
NTSTATUS status,
- const char *name, DOM_SID *sid,
+ const char *name, const DOM_SID *sid,
enum SID_NAME_USE type)
{
struct cache_entry *centry;
return;
centry_put_sid(centry, sid);
fstrcpy(uname, name);
- strupper(uname);
+ strupper_m(uname);
centry_end(centry, "NS/%s", sid_to_string(sid_string, sid));
DEBUG(10,("wcache_save_name_to_sid: %s -> %s\n", uname, sid_string));
centry_free(centry);
}
static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
- DOM_SID *sid, const char *name, enum SID_NAME_USE type)
+ const DOM_SID *sid, const char *name, enum SID_NAME_USE type)
{
struct cache_entry *centry;
fstring sid_string;
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
- unsigned int i;
+ unsigned int i, retry;
if (!cache->tdb)
goto do_query;
*num_entries = 0;
*info = NULL;
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* Return status value returned by seq number check */
- DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
- domain->name ));
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
- status = cache->backend->query_user_list(domain, mem_ctx, num_entries, info);
+ /* Put the query_user_list() in a retry loop. There appears to be
+ * some bug either with Windows 2000 or Samba's handling of large
+ * rpc replies. This manifests itself as sudden disconnection
+ * at a random point in the enumeration of a large (60k) user list.
+ * The retry loop simply tries the operation again. )-: It's not
+ * pretty but an acceptable workaround until we work out what the
+ * real problem is. */
+
+ retry = 0;
+ do {
+
+ DEBUG(10,("query_user_list: [Cached] - doing backend query for list for domain %s\n",
+ domain->name ));
+
+ status = domain->backend->query_user_list(domain, mem_ctx, num_entries, info);
+ if (!NT_STATUS_IS_OK(status))
+ DEBUG(3, ("query_user_list: returned 0x%08x, retrying\n", NT_STATUS_V(status)));
+ if (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL)) {
+ DEBUG(3, ("query_user_list: flushing connection cache\n"));
+ winbindd_cm_flush();
+ }
+
+ } while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
+ (retry++ < 5));
/* and save it */
refresh_sequence_number(domain, False);
centry_put_string(centry, (*info)[i].full_name);
centry_put_sid(centry, (*info)[i].user_sid);
centry_put_sid(centry, (*info)[i].group_sid);
- if (cache->backend->consistent) {
+ if (domain->backend->consistent) {
/* when the backend is consistent we can pre-prime some mappings */
wcache_save_name_to_sid(domain, NT_STATUS_OK,
(*info)[i].acct_name,
*num_entries = 0;
*info = NULL;
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
DEBUG(10,("enum_dom_groups: [Cached] - doing backend query for list for domain %s\n",
domain->name ));
- status = cache->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
+ status = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
/* and save it */
refresh_sequence_number(domain, False);
*num_entries = 0;
*info = NULL;
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
DEBUG(10,("enum_local_groups: [Cached] - doing backend query for list for domain %s\n",
domain->name ));
- status = cache->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
+ status = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
/* and save it */
refresh_sequence_number(domain, False);
fstring uname;
DOM_SID *sid2;
- if (!cache->tdb) goto do_query;
+ if (!cache->tdb)
+ goto do_query;
fstrcpy(uname, name);
- strupper(uname);
+ strupper_m(uname);
centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
if (!centry)
goto do_query;
- *type = centry_uint32(centry);
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
sid2 = centry_sid(centry, mem_ctx);
if (!sid2) {
ZERO_STRUCTP(sid);
do_query:
ZERO_STRUCTP(sid);
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * name-to-sid that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
DEBUG(10,("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
domain->name ));
- status = cache->backend->name_to_sid(domain, mem_ctx, name, sid, type);
+ status = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
/* and save it */
wcache_save_name_to_sid(domain, status, name, sid, *type);
given */
static NTSTATUS sid_to_name(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
- DOM_SID *sid,
+ const DOM_SID *sid,
char **name,
enum SID_NAME_USE *type)
{
if (!centry)
goto do_query;
if (NT_STATUS_IS_OK(centry->status)) {
- *type = centry_uint32(centry);
+ *type = (enum SID_NAME_USE)centry_uint32(centry);
*name = centry_string(centry, mem_ctx);
}
status = centry->status;
do_query:
*name = NULL;
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* If the seq number check indicated that there is a problem
+ * with this DC, then return that status... except for
+ * access_denied. This is special because the dc may be in
+ * "restrict anonymous = 1" mode, in which case it will deny
+ * most unauthenticated operations, but *will* allow the LSA
+ * sid-to-name that we try as a fallback. */
+
+ if (!(NT_STATUS_IS_OK(domain->last_status)
+ || NT_STATUS_EQUAL(domain->last_status, NT_STATUS_ACCESS_DENIED)))
+ return domain->last_status;
DEBUG(10,("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
domain->name ));
- status = cache->backend->sid_to_name(domain, mem_ctx, sid, name, type);
+ status = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
/* and save it */
refresh_sequence_number(domain, False);
/* Lookup user information from a rid */
static NTSTATUS query_user(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
- DOM_SID *user_sid,
+ const DOM_SID *user_sid,
WINBIND_USERINFO *info)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
- fstring sid_string;
if (!cache->tdb)
goto do_query;
- centry = wcache_fetch(cache, domain, "U/%s", sid_to_string(sid_string, user_sid));
+ centry = wcache_fetch(cache, domain, "U/%s", sid_string_static(user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
if (!centry)
goto do_query;
do_query:
ZERO_STRUCTP(info);
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
DEBUG(10,("sid_to_name: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
- status = cache->backend->query_user(domain, mem_ctx, user_sid, info);
+ status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
/* and save it */
refresh_sequence_number(domain, False);
/* Lookup groups a user is a member of. */
static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
- DOM_SID *user_sid,
+ const DOM_SID *user_sid,
uint32 *num_groups, DOM_SID ***user_gids)
{
struct winbind_cache *cache = get_cache(domain);
goto do_query;
centry = wcache_fetch(cache, domain, "UG/%s", sid_to_string(sid_string, user_sid));
+
+ /* If we have an access denied cache entry and a cached info3 in the
+ samlogon cache then do a query. This will force the rpc back end
+ to return the info3 data. */
+
+ if (NT_STATUS_V(domain->last_status) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED) &&
+ netsamlogon_cache_have(user_sid)) {
+ DEBUG(10, ("query_user: cached access denied and have cached info3\n"));
+ domain->last_status = NT_STATUS_OK;
+ centry_free(centry);
+ goto do_query;
+ }
+
if (!centry)
goto do_query;
(*num_groups) = 0;
(*user_gids) = NULL;
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ /* Return status value returned by seq number check */
+
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
DEBUG(10,("lookup_usergroups: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
- status = cache->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+ status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
/* and save it */
refresh_sequence_number(domain, False);
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
- DOM_SID *group_sid, uint32 *num_names,
+ const DOM_SID *group_sid, uint32 *num_names,
DOM_SID ***sid_mem, char ***names,
uint32 **name_types)
{
(*names) = NULL;
(*name_types) = NULL;
+ /* Return status value returned by seq number check */
- if (wcache_server_down(domain)) {
- return NT_STATUS_SERVER_DISABLED;
- }
+ if (!NT_STATUS_IS_OK(domain->last_status))
+ return domain->last_status;
DEBUG(10,("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
- status = cache->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
- sid_mem, names, name_types);
+ status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
+ sid_mem, names, name_types);
/* and save it */
refresh_sequence_number(domain, False);
char ***alt_names,
DOM_SID **dom_sids)
{
- struct winbind_cache *cache = get_cache(domain);
+ get_cache(domain);
DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
/* we don't cache this call */
- return cache->backend->trusted_domains(domain, mem_ctx, num_domains,
+ return domain->backend->trusted_domains(domain, mem_ctx, num_domains,
names, alt_names, dom_sids);
}
/* find the domain sid */
static NTSTATUS domain_sid(struct winbindd_domain *domain, DOM_SID *sid)
{
- struct winbind_cache *cache = get_cache(domain);
+ get_cache(domain);
DEBUG(10,("domain_sid: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
/* we don't cache this call */
- return cache->backend->domain_sid(domain, sid);
+ return domain->backend->domain_sid(domain, sid);
}
/* find the alternate names for the domain, if any */
static NTSTATUS alternate_name(struct winbindd_domain *domain)
{
- struct winbind_cache *cache = get_cache(domain);
+ get_cache(domain);
DEBUG(10,("alternate_name: [Cached] - doing backend query for info for domain %s\n",
domain->name ));
/* we don't cache this call */
- return cache->backend->alternate_name(domain);
+ return domain->backend->alternate_name(domain);
+}
+
+/* Invalidate cached user and group lists coherently */
+
+static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ if (strncmp(kbuf.dptr, "UL/", 3) == 0 ||
+ strncmp(kbuf.dptr, "GL/", 3) == 0)
+ tdb_delete(the_tdb, kbuf);
+
+ return 0;
+}
+
+/* Invalidate the getpwnam and getgroups entries for a winbindd domain */
+
+void wcache_invalidate_samlogon(struct winbindd_domain *domain,
+ NET_USER_INFO_3 *info3)
+{
+ struct winbind_cache *cache;
+
+ if (!domain)
+ return;
+
+ cache = get_cache(domain);
+ netsamlogon_clear_cached_user(cache->tdb, info3);
+}
+
+void wcache_invalidate_cache(void)
+{
+ struct winbindd_domain *domain;
+
+ for (domain = domain_list(); domain; domain = domain->next) {
+ struct winbind_cache *cache = get_cache(domain);
+
+ DEBUG(10, ("wcache_invalidate_cache: invalidating cache "
+ "entries for %s\n", domain->name));
+ if (cache)
+ tdb_traverse(cache->tdb, traverse_fn, NULL);
+ }
}
/* the ADS backend methods are exposed via this structure */