1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-28 17:47:29 +03:00
samba-mirror/source3/nsswitch/winbindd_cache.c
Gerald Carter fe48d1c800 * Revert to using rpc for mixed mode AD domains.
The reason for this are:
  (a) the set_dc_type_and_flags() cannot tell the different
      between connecting to an NT4 domain and an NT4 BDC
      of a mixed mode domain.
  (b) the connection management for the rpc backend only
      provides on named pipe per cli_state.  So it is possible
      to connect to an NT4 BDC for netlogon and an AD mixed mode
      DC for lsarpc.  RPC is the lowest common demonimator here.
  (c) Issue with the sequence number value between the
      highestCommittedUSN LDAP attribute and the seq_num returned
      via RPC.


We will revisit this later, but the changes need to make this
work right now are too broad and risky.
(This used to be commit 1ed2e521536108229d153c2996f4757d89461166)
2004-01-14 16:25:39 +00:00

1376 lines
35 KiB
C

/*
Unix SMB/CIFS implementation.
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
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 "includes.h"
#include "winbindd.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
struct winbind_cache {
TDB_CONTEXT *tdb;
};
struct cache_entry {
NTSTATUS status;
uint32 sequence_number;
uint8 *data;
uint32 len, ofs;
};
#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
static struct winbind_cache *wcache;
/* flush the cache */
void wcache_flush_cache(void)
{
extern BOOL opt_nocache;
if (!wcache)
return;
if (wcache->tdb) {
tdb_close(wcache->tdb);
wcache->tdb = NULL;
}
if (opt_nocache)
return;
wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 5000,
TDB_CLEAR_IF_FIRST, O_RDWR|O_CREAT, 0600);
if (!wcache->tdb) {
DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
}
DEBUG(10,("wcache_flush_cache success\n"));
}
void winbindd_check_cache_size(time_t t)
{
static time_t last_check_time;
struct stat st;
if (last_check_time == (time_t)0)
last_check_time = t;
if (t - last_check_time < 60 && t - last_check_time > 0)
return;
if (wcache == NULL || wcache->tdb == NULL) {
DEBUG(0, ("Unable to check size of tdb cache - cache not open !\n"));
return;
}
if (fstat(wcache->tdb->fd, &st) == -1) {
DEBUG(0, ("Unable to check size of tdb cache %s!\n", strerror(errno) ));
return;
}
if (st.st_size > WINBINDD_MAX_CACHE_SIZE) {
DEBUG(10,("flushing cache due to size (%lu) > (%lu)\n",
(unsigned long)st.st_size,
(unsigned long)WINBINDD_MAX_CACHE_SIZE));
wcache_flush_cache();
}
}
/* get the winbind_cache structure */
static struct winbind_cache *get_cache(struct winbindd_domain *domain)
{
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);
wcache = ret;
wcache_flush_cache();
return ret;
}
/*
free a centry structure
*/
static void centry_free(struct cache_entry *centry)
{
if (!centry)
return;
SAFE_FREE(centry->data);
free(centry);
}
/*
pull a uint32 from a cache entry
*/
static uint32 centry_uint32(struct cache_entry *centry)
{
uint32 ret;
if (centry->len - centry->ofs < 4) {
DEBUG(0,("centry corruption? needed 4 bytes, have %d\n",
centry->len - centry->ofs));
smb_panic("centry_uint32");
}
ret = IVAL(centry->data, centry->ofs);
centry->ofs += 4;
return ret;
}
/*
pull a uint8 from a cache entry
*/
static uint8 centry_uint8(struct cache_entry *centry)
{
uint8 ret;
if (centry->len - centry->ofs < 1) {
DEBUG(0,("centry corruption? needed 1 bytes, have %d\n",
centry->len - centry->ofs));
smb_panic("centry_uint32");
}
ret = CVAL(centry->data, centry->ofs);
centry->ofs += 1;
return ret;
}
/* pull a string from a cache entry, using the supplied
talloc context
*/
static char *centry_string(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
{
uint32 len;
char *ret;
len = centry_uint8(centry);
if (len == 0xFF) {
/* a deliberate NULL string */
return NULL;
}
if (centry->len - centry->ofs < len) {
DEBUG(0,("centry corruption? needed %d bytes, have %d\n",
len, centry->len - centry->ofs));
smb_panic("centry_string");
}
ret = talloc(mem_ctx, len+1);
if (!ret) {
smb_panic("centry_string out of memory\n");
}
memcpy(ret,centry->data + centry->ofs, len);
ret[len] = 0;
centry->ofs += len;
return ret;
}
/* pull a string from a cache entry, using the supplied
talloc context
*/
static DOM_SID *centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
{
DOM_SID *sid;
char *sid_string;
sid = talloc(mem_ctx, sizeof(*sid));
if (!sid)
return NULL;
sid_string = centry_string(centry, mem_ctx);
if (!string_to_sid(sid, sid_string)) {
return NULL;
}
return sid;
}
/* the server is considered down if it can't give us a sequence number */
static BOOL wcache_server_down(struct winbindd_domain *domain)
{
BOOL ret;
if (!wcache->tdb)
return False;
ret = (domain->sequence_number == DOM_SEQUENCE_NONE);
if (ret)
DEBUG(10,("wcache_server_down: server for Domain %s down\n",
domain->name ));
return ret;
}
static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
{
TDB_DATA data;
fstring key;
uint32 time_diff;
if (!wcache->tdb) {
DEBUG(10,("fetch_cache_seqnum: tdb == NULL\n"));
return NT_STATUS_UNSUCCESSFUL;
}
fstr_sprintf( key, "SEQNUM/%s", domain->name );
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;
}
domain->sequence_number = IVAL(data.dptr, 0);
domain->last_seq_check = IVAL(data.dptr, 4);
/* have we expired? */
time_diff = now - domain->last_seq_check;
if ( time_diff > lp_winbind_cache_time() ) {
DEBUG(10,("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
domain->name, domain->sequence_number,
(uint32)domain->last_seq_check));
return NT_STATUS_UNSUCCESSFUL;
}
DEBUG(10,("fetch_cache_seqnum: success [%s][%u @ %u]\n",
domain->name, domain->sequence_number,
(uint32)domain->last_seq_check));
return NT_STATUS_OK;
}
static NTSTATUS store_cache_seqnum( struct winbindd_domain *domain )
{
TDB_DATA data, key;
fstring key_str;
char buf[8];
if (!wcache->tdb) {
DEBUG(10,("store_cache_seqnum: tdb == NULL\n"));
return NT_STATUS_UNSUCCESSFUL;
}
fstr_sprintf( key_str, "SEQNUM/%s", domain->name );
key.dptr = key_str;
key.dsize = strlen(key_str)+1;
SIVAL(buf, 0, domain->sequence_number);
SIVAL(buf, 4, domain->last_seq_check);
data.dptr = buf;
data.dsize = 8;
if ( tdb_store( wcache->tdb, key, data, TDB_REPLACE) == -1 ) {
DEBUG(10,("store_cache_seqnum: tdb_store fail key [%s]\n", key_str ));
return NT_STATUS_UNSUCCESSFUL;
}
DEBUG(10,("store_cache_seqnum: success [%s][%u @ %u]\n",
domain->name, domain->sequence_number,
(uint32)domain->last_seq_check));
return NT_STATUS_OK;
}
/*
refresh the domain sequence number. If force is True
then always refresh it, no matter how recently we fetched it
*/
static void refresh_sequence_number(struct winbindd_domain *domain, BOOL force)
{
NTSTATUS status;
unsigned time_diff;
time_t t = time(NULL);
unsigned cache_time = lp_winbind_cache_time();
get_cache( domain );
/* trying to reconnect is expensive, don't do it too often */
if (domain->sequence_number == DOM_SEQUENCE_NONE) {
cache_time *= 8;
}
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));
goto done;
}
/* try to get the sequence number from the tdb cache first */
/* this will update the timestamp as well */
status = fetch_cache_seqnum( domain, t );
if ( NT_STATUS_IS_OK(status) )
goto done;
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 */
store_cache_seqnum( domain );
done:
DEBUG(10, ("refresh_sequence_number: %s seq number is now %d\n",
domain->name, domain->sequence_number));
return;
}
/*
decide if a cache entry has expired
*/
static BOOL centry_expired(struct winbindd_domain *domain, const char *keystr, struct cache_entry *centry)
{
/* if the server is OK and our cache entry came from when it was down then
the entry is invalid */
if (domain->sequence_number != DOM_SEQUENCE_NONE &&
centry->sequence_number == DOM_SEQUENCE_NONE) {
DEBUG(10,("centry_expired: Key %s for domain %s invalid sequence.\n",
keystr, domain->name ));
return True;
}
/* if the server is down or the cache entry is not older than the
current sequence number then it is OK */
if (wcache_server_down(domain) ||
centry->sequence_number == domain->sequence_number) {
DEBUG(10,("centry_expired: Key %s for domain %s is good.\n",
keystr, domain->name ));
return False;
}
DEBUG(10,("centry_expired: Key %s for domain %s expired\n",
keystr, domain->name ));
/* it's expired */
return True;
}
/*
fetch an entry from the cache, with a varargs key. auto-fetch the sequence
number and return status
*/
static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
struct winbindd_domain *domain,
const char *format, ...) PRINTF_ATTRIBUTE(3,4);
static struct cache_entry *wcache_fetch(struct winbind_cache *cache,
struct winbindd_domain *domain,
const char *format, ...)
{
va_list ap;
char *kstr;
TDB_DATA data;
struct cache_entry *centry;
TDB_DATA key;
refresh_sequence_number(domain, False);
va_start(ap, format);
smb_xvasprintf(&kstr, format, ap);
va_end(ap);
key.dptr = kstr;
key.dsize = strlen(kstr);
data = tdb_fetch(wcache->tdb, key);
if (!data.dptr) {
/* a cache miss */
free(kstr);
return NULL;
}
centry = smb_xmalloc(sizeof(*centry));
centry->data = (unsigned char *)data.dptr;
centry->len = data.dsize;
centry->ofs = 0;
if (centry->len < 8) {
/* huh? corrupt cache? */
DEBUG(10,("wcache_fetch: Corrupt cache for key %s domain %s (len < 8) ?\n",
kstr, domain->name ));
centry_free(centry);
free(kstr);
return NULL;
}
centry->status = NT_STATUS(centry_uint32(centry));
centry->sequence_number = centry_uint32(centry);
if (centry_expired(domain, kstr, centry)) {
extern BOOL opt_dual_daemon;
DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
kstr, domain->name ));
if (opt_dual_daemon) {
extern BOOL background_process;
background_process = True;
DEBUG(10,("wcache_fetch: background processing expired entry %s for domain %s\n",
kstr, domain->name ));
} else {
centry_free(centry);
free(kstr);
return NULL;
}
}
DEBUG(10,("wcache_fetch: returning entry %s for domain %s\n",
kstr, domain->name ));
free(kstr);
return centry;
}
/*
make sure we have at least len bytes available in a centry
*/
static void centry_expand(struct cache_entry *centry, uint32 len)
{
uint8 *p;
if (centry->len - centry->ofs >= len)
return;
centry->len *= 2;
p = realloc(centry->data, centry->len);
if (!p) {
DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
smb_panic("out of memory in centry_expand");
}
centry->data = p;
}
/*
push a uint32 into a centry
*/
static void centry_put_uint32(struct cache_entry *centry, uint32 v)
{
centry_expand(centry, 4);
SIVAL(centry->data, centry->ofs, v);
centry->ofs += 4;
}
/*
push a uint8 into a centry
*/
static void centry_put_uint8(struct cache_entry *centry, uint8 v)
{
centry_expand(centry, 1);
SCVAL(centry->data, centry->ofs, v);
centry->ofs += 1;
}
/*
push a string into a centry
*/
static void centry_put_string(struct cache_entry *centry, const char *s)
{
int len;
if (!s) {
/* null strings are marked as len 0xFFFF */
centry_put_uint8(centry, 0xFF);
return;
}
len = strlen(s);
/* can't handle more than 254 char strings. Truncating is probably best */
if (len > 254)
len = 254;
centry_put_uint8(centry, len);
centry_expand(centry, len);
memcpy(centry->data + centry->ofs, s, len);
centry->ofs += len;
}
static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid)
{
fstring sid_string;
centry_put_string(centry, sid_to_string(sid_string, sid));
}
/*
start a centry for output. When finished, call centry_end()
*/
struct cache_entry *centry_start(struct winbindd_domain *domain, NTSTATUS status)
{
struct cache_entry *centry;
if (!wcache->tdb)
return NULL;
centry = smb_xmalloc(sizeof(*centry));
centry->len = 8192; /* reasonable default */
centry->data = smb_xmalloc(centry->len);
centry->ofs = 0;
centry->sequence_number = domain->sequence_number;
centry_put_uint32(centry, NT_STATUS_V(status));
centry_put_uint32(centry, centry->sequence_number);
return centry;
}
/*
finish a centry and write it to the tdb
*/
static void centry_end(struct cache_entry *centry, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
static void centry_end(struct cache_entry *centry, const char *format, ...)
{
va_list ap;
char *kstr;
TDB_DATA key, data;
va_start(ap, format);
smb_xvasprintf(&kstr, format, ap);
va_end(ap);
key.dptr = kstr;
key.dsize = strlen(kstr);
data.dptr = (char *)centry->data;
data.dsize = centry->ofs;
tdb_store(wcache->tdb, key, data, TDB_REPLACE);
free(kstr);
}
static void wcache_save_name_to_sid(struct winbindd_domain *domain,
NTSTATUS status,
const char *name, const DOM_SID *sid,
enum SID_NAME_USE type)
{
struct cache_entry *centry;
fstring uname;
fstring sid_string;
centry = centry_start(domain, status);
if (!centry)
return;
centry_put_sid(centry, sid);
fstrcpy(uname, name);
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,
const DOM_SID *sid, const char *name, enum SID_NAME_USE type)
{
struct cache_entry *centry;
fstring sid_string;
centry = centry_start(domain, status);
if (!centry)
return;
if (NT_STATUS_IS_OK(status)) {
centry_put_uint32(centry, type);
centry_put_string(centry, name);
}
centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
centry_free(centry);
}
static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
{
struct cache_entry *centry;
fstring sid_string;
centry = centry_start(domain, status);
if (!centry)
return;
centry_put_string(centry, info->acct_name);
centry_put_string(centry, info->full_name);
centry_put_sid(centry, info->user_sid);
centry_put_sid(centry, info->group_sid);
centry_end(centry, "U/%s", sid_to_string(sid_string, info->user_sid));
DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
centry_free(centry);
}
/* Query display info. This is the basic user list fn */
static NTSTATUS query_user_list(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_entries,
WINBIND_USERINFO **info)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
unsigned int i, retry;
if (!cache->tdb)
goto do_query;
centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
if (!centry)
goto do_query;
*num_entries = centry_uint32(centry);
if (*num_entries == 0)
goto do_cached;
(*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
if (! (*info))
smb_panic("query_user_list out of memory");
for (i=0; i<(*num_entries); i++) {
(*info)[i].acct_name = centry_string(centry, mem_ctx);
(*info)[i].full_name = centry_string(centry, mem_ctx);
(*info)[i].user_sid = centry_sid(centry, mem_ctx);
(*info)[i].group_sid = centry_sid(centry, mem_ctx);
}
do_cached:
status = centry->status;
DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
*num_entries = 0;
*info = NULL;
/* Return status value returned by seq number check */
if (!NT_STATUS_IS_OK(domain->last_status))
return domain->last_status;
/* 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 = centry_start(domain, status);
if (!centry)
goto skip_save;
centry_put_uint32(centry, *num_entries);
for (i=0; i<(*num_entries); i++) {
centry_put_string(centry, (*info)[i].acct_name);
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 (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,
(*info)[i].user_sid,
SID_NAME_USER);
wcache_save_sid_to_name(domain, NT_STATUS_OK,
(*info)[i].user_sid,
(*info)[i].acct_name,
SID_NAME_USER);
wcache_save_user(domain, NT_STATUS_OK, &(*info)[i]);
}
}
centry_end(centry, "UL/%s", domain->name);
centry_free(centry);
skip_save:
return status;
}
/* list all domain groups */
static NTSTATUS enum_dom_groups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_entries,
struct acct_info **info)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
unsigned int i;
if (!cache->tdb)
goto do_query;
centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
if (!centry)
goto do_query;
*num_entries = centry_uint32(centry);
if (*num_entries == 0)
goto do_cached;
(*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
if (! (*info))
smb_panic("enum_dom_groups out of memory");
for (i=0; i<(*num_entries); i++) {
fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
(*info)[i].rid = centry_uint32(centry);
}
do_cached:
status = centry->status;
DEBUG(10,("enum_dom_groups: [Cached] - cached list for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
*num_entries = 0;
*info = NULL;
/* 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 = domain->backend->enum_dom_groups(domain, mem_ctx, num_entries, info);
/* and save it */
refresh_sequence_number(domain, False);
centry = centry_start(domain, status);
if (!centry)
goto skip_save;
centry_put_uint32(centry, *num_entries);
for (i=0; i<(*num_entries); i++) {
centry_put_string(centry, (*info)[i].acct_name);
centry_put_string(centry, (*info)[i].acct_desc);
centry_put_uint32(centry, (*info)[i].rid);
}
centry_end(centry, "GL/%s/domain", domain->name);
centry_free(centry);
skip_save:
return status;
}
/* list all domain groups */
static NTSTATUS enum_local_groups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_entries,
struct acct_info **info)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
unsigned int i;
if (!cache->tdb)
goto do_query;
centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
if (!centry)
goto do_query;
*num_entries = centry_uint32(centry);
if (*num_entries == 0)
goto do_cached;
(*info) = talloc(mem_ctx, sizeof(**info) * (*num_entries));
if (! (*info))
smb_panic("enum_dom_groups out of memory");
for (i=0; i<(*num_entries); i++) {
fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
fstrcpy((*info)[i].acct_desc, centry_string(centry, mem_ctx));
(*info)[i].rid = centry_uint32(centry);
}
do_cached:
/* If we are returning cached data and the domain controller
is down then we don't know whether the data is up to date
or not. Return NT_STATUS_MORE_PROCESSING_REQUIRED to
indicate this. */
if (wcache_server_down(domain)) {
DEBUG(10, ("enum_local_groups: returning cached user list and server was down\n"));
status = NT_STATUS_MORE_PROCESSING_REQUIRED;
} else
status = centry->status;
DEBUG(10,("enum_local_groups: [Cached] - cached list for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
*num_entries = 0;
*info = NULL;
/* 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 = domain->backend->enum_local_groups(domain, mem_ctx, num_entries, info);
/* and save it */
refresh_sequence_number(domain, False);
centry = centry_start(domain, status);
if (!centry)
goto skip_save;
centry_put_uint32(centry, *num_entries);
for (i=0; i<(*num_entries); i++) {
centry_put_string(centry, (*info)[i].acct_name);
centry_put_string(centry, (*info)[i].acct_desc);
centry_put_uint32(centry, (*info)[i].rid);
}
centry_end(centry, "GL/%s/local", domain->name);
centry_free(centry);
skip_save:
return status;
}
/* convert a single name to a sid in a domain */
static NTSTATUS name_to_sid(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
const char *name,
DOM_SID *sid,
enum SID_NAME_USE *type)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
fstring uname;
DOM_SID *sid2;
if (!cache->tdb)
goto do_query;
fstrcpy(uname, name);
strupper_m(uname);
centry = wcache_fetch(cache, domain, "NS/%s/%s", domain->name, uname);
if (!centry)
goto do_query;
*type = (enum SID_NAME_USE)centry_uint32(centry);
sid2 = centry_sid(centry, mem_ctx);
if (!sid2) {
ZERO_STRUCTP(sid);
} else {
sid_copy(sid, sid2);
}
status = centry->status;
DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
ZERO_STRUCTP(sid);
/* 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 = domain->backend->name_to_sid(domain, mem_ctx, name, sid, type);
/* and save it */
wcache_save_name_to_sid(domain, status, name, sid, *type);
/* We can't save the sid to name mapping as we don't know the
correct case of the name without looking it up */
return status;
}
/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
given */
static NTSTATUS sid_to_name(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
const DOM_SID *sid,
char **name,
enum SID_NAME_USE *type)
{
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, "SN/%s", sid_to_string(sid_string, sid));
if (!centry)
goto do_query;
if (NT_STATUS_IS_OK(centry->status)) {
*type = (enum SID_NAME_USE)centry_uint32(centry);
*name = centry_string(centry, mem_ctx);
}
status = centry->status;
DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
*name = NULL;
/* 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 = domain->backend->sid_to_name(domain, mem_ctx, sid, name, type);
/* and save it */
refresh_sequence_number(domain, False);
wcache_save_sid_to_name(domain, status, sid, *name, *type);
wcache_save_name_to_sid(domain, status, *name, sid, *type);
return status;
}
/* Lookup user information from a rid */
static NTSTATUS query_user(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
DOM_SID *user_sid,
WINBIND_USERINFO *info)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
if (!cache->tdb)
goto do_query;
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;
info->acct_name = centry_string(centry, mem_ctx);
info->full_name = centry_string(centry, mem_ctx);
info->user_sid = centry_sid(centry, mem_ctx);
info->group_sid = centry_sid(centry, mem_ctx);
status = centry->status;
DEBUG(10,("query_user: [Cached] - cached info for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
ZERO_STRUCTP(info);
/* 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 = domain->backend->query_user(domain, mem_ctx, user_sid, info);
/* and save it */
refresh_sequence_number(domain, False);
wcache_save_user(domain, status, info);
return status;
}
/* Lookup groups a user is a member of. */
static NTSTATUS lookup_usergroups(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
DOM_SID *user_sid,
uint32 *num_groups, DOM_SID ***user_gids)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
unsigned int i;
fstring sid_string;
if (!cache->tdb)
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 = centry_uint32(centry);
if (*num_groups == 0)
goto do_cached;
(*user_gids) = talloc(mem_ctx, sizeof(**user_gids) * (*num_groups));
if (! (*user_gids))
smb_panic("lookup_usergroups out of memory");
for (i=0; i<(*num_groups); i++) {
(*user_gids)[i] = centry_sid(centry, mem_ctx);
}
do_cached:
status = centry->status;
DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
(*num_groups) = 0;
(*user_gids) = NULL;
/* 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 = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
/* and save it */
refresh_sequence_number(domain, False);
centry = centry_start(domain, status);
if (!centry)
goto skip_save;
centry_put_uint32(centry, *num_groups);
for (i=0; i<(*num_groups); i++) {
centry_put_sid(centry, (*user_gids)[i]);
}
centry_end(centry, "UG/%s", sid_to_string(sid_string, user_sid));
centry_free(centry);
skip_save:
return status;
}
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
DOM_SID *group_sid, uint32 *num_names,
DOM_SID ***sid_mem, char ***names,
uint32 **name_types)
{
struct winbind_cache *cache = get_cache(domain);
struct cache_entry *centry = NULL;
NTSTATUS status;
unsigned int i;
fstring sid_string;
if (!cache->tdb)
goto do_query;
centry = wcache_fetch(cache, domain, "GM/%s", sid_to_string(sid_string, group_sid));
if (!centry)
goto do_query;
*num_names = centry_uint32(centry);
if (*num_names == 0)
goto do_cached;
(*sid_mem) = talloc(mem_ctx, sizeof(**sid_mem) * (*num_names));
(*names) = talloc(mem_ctx, sizeof(**names) * (*num_names));
(*name_types) = talloc(mem_ctx, sizeof(**name_types) * (*num_names));
if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
smb_panic("lookup_groupmem out of memory");
}
for (i=0; i<(*num_names); i++) {
(*sid_mem)[i] = centry_sid(centry, mem_ctx);
(*names)[i] = centry_string(centry, mem_ctx);
(*name_types)[i] = centry_uint32(centry);
}
do_cached:
status = centry->status;
DEBUG(10,("lookup_groupmem: [Cached] - cached info for domain %s status %s\n",
domain->name, get_friendly_nt_error_msg(status) ));
centry_free(centry);
return status;
do_query:
(*num_names) = 0;
(*sid_mem) = NULL;
(*names) = NULL;
(*name_types) = NULL;
/* Return status value returned by seq number check */
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 = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid, num_names,
sid_mem, names, name_types);
/* and save it */
refresh_sequence_number(domain, False);
centry = centry_start(domain, status);
if (!centry)
goto skip_save;
centry_put_uint32(centry, *num_names);
for (i=0; i<(*num_names); i++) {
centry_put_sid(centry, (*sid_mem)[i]);
centry_put_string(centry, (*names)[i]);
centry_put_uint32(centry, (*name_types)[i]);
}
centry_end(centry, "GM/%s", sid_to_string(sid_string, group_sid));
centry_free(centry);
skip_save:
return status;
}
/* find the sequence number for a domain */
static NTSTATUS sequence_number(struct winbindd_domain *domain, uint32 *seq)
{
refresh_sequence_number(domain, False);
*seq = domain->sequence_number;
return NT_STATUS_OK;
}
/* enumerate trusted domains */
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
TALLOC_CTX *mem_ctx,
uint32 *num_domains,
char ***names,
char ***alt_names,
DOM_SID **dom_sids)
{
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 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)
{
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 domain->backend->domain_sid(domain, sid);
}
/* find the alternate names for the domain, if any */
static NTSTATUS alternate_name(struct winbindd_domain *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 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 */
struct winbindd_methods cache_methods = {
True,
query_user_list,
enum_dom_groups,
enum_local_groups,
name_to_sid,
sid_to_name,
query_user,
lookup_usergroups,
lookup_groupmem,
sequence_number,
trusted_domains,
domain_sid,
alternate_name
};