mirror of
				https://github.com/samba-team/samba.git
				synced 2025-11-04 00:23:49 +03:00 
			
		
		
		
	Clients can request name-to-sid queries for different combinations of upper and lower case names. We don't want to create the reverse caching entries for each combination used. This avoids inconsistent answers on sid-to-name queries. Please review! Karolin
		
			
				
	
	
		
			3983 lines
		
	
	
		
			96 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			3983 lines
		
	
	
		
			96 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 
 | 
						|
   Unix SMB/CIFS implementation.
 | 
						|
 | 
						|
   Winbind cache backend functions
 | 
						|
 | 
						|
   Copyright (C) Andrew Tridgell 2001
 | 
						|
   Copyright (C) Gerald Carter   2003-2007
 | 
						|
   Copyright (C) Volker Lendecke 2005
 | 
						|
   Copyright (C) Guenther Deschner 2005
 | 
						|
   Copyright (C) Michael Adam    2007
 | 
						|
 | 
						|
   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 3 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, see <http://www.gnu.org/licenses/>.
 | 
						|
*/
 | 
						|
 | 
						|
#include "includes.h"
 | 
						|
#include "winbindd.h"
 | 
						|
 | 
						|
#undef DBGC_CLASS
 | 
						|
#define DBGC_CLASS DBGC_WINBIND
 | 
						|
 | 
						|
#define WINBINDD_CACHE_VERSION 1
 | 
						|
#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
 | 
						|
 | 
						|
extern struct winbindd_methods reconnect_methods;
 | 
						|
extern bool opt_nocache;
 | 
						|
#ifdef HAVE_ADS
 | 
						|
extern struct winbindd_methods ads_methods;
 | 
						|
#endif
 | 
						|
extern struct winbindd_methods builtin_passdb_methods;
 | 
						|
 | 
						|
/*
 | 
						|
 * JRA. KEEP THIS LIST UP TO DATE IF YOU ADD CACHE ENTRIES.
 | 
						|
 * Here are the list of entry types that are *not* stored
 | 
						|
 * as form struct cache_entry in the cache.
 | 
						|
 */
 | 
						|
 | 
						|
static const char *non_centry_keys[] = {
 | 
						|
	"SEQNUM/",
 | 
						|
	"DR/",
 | 
						|
	"DE/",
 | 
						|
	"WINBINDD_OFFLINE",
 | 
						|
	WINBINDD_CACHE_VERSION_KEYSTR,
 | 
						|
	NULL
 | 
						|
};
 | 
						|
 | 
						|
/************************************************************************
 | 
						|
 Is this key a non-centry type ?
 | 
						|
************************************************************************/
 | 
						|
 | 
						|
static bool is_non_centry_key(TDB_DATA kbuf)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (kbuf.dptr == NULL || kbuf.dsize == 0) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	for (i = 0; non_centry_keys[i] != NULL; i++) {
 | 
						|
		size_t namelen = strlen(non_centry_keys[i]);
 | 
						|
		if (kbuf.dsize < namelen) {
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (strncmp(non_centry_keys[i], (const char *)kbuf.dptr, namelen) == 0) {
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
/* Global online/offline state - False when online. winbindd starts up online
 | 
						|
   and sets this to true if the first query fails and there's an entry in
 | 
						|
   the cache tdb telling us to stay offline. */
 | 
						|
 | 
						|
static bool global_winbindd_offline_state;
 | 
						|
 | 
						|
struct winbind_cache {
 | 
						|
	TDB_CONTEXT *tdb;
 | 
						|
};
 | 
						|
 | 
						|
struct cache_entry {
 | 
						|
	NTSTATUS status;
 | 
						|
	uint32 sequence_number;
 | 
						|
	uint8 *data;
 | 
						|
	uint32 len, ofs;
 | 
						|
};
 | 
						|
 | 
						|
void (*smb_panic_fn)(const char *const why) = smb_panic;
 | 
						|
 | 
						|
#define WINBINDD_MAX_CACHE_SIZE (50*1024*1024)
 | 
						|
 | 
						|
static struct winbind_cache *wcache;
 | 
						|
 | 
						|
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(tdb_fd(wcache->tdb), &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;
 | 
						|
 | 
						|
	/* We have to know what type of domain we are dealing with first. */
 | 
						|
 | 
						|
	if (domain->internal) {
 | 
						|
		domain->backend = &builtin_passdb_methods;
 | 
						|
		domain->initialized = True;
 | 
						|
	}
 | 
						|
	if ( !domain->initialized ) {
 | 
						|
		init_dc_connection( domain );
 | 
						|
	}
 | 
						|
 | 
						|
	/* 
 | 
						|
	   OK.  listen up becasue I'm only going to say this once.
 | 
						|
	   We have the following scenarios to consider
 | 
						|
	   (a) trusted AD domains on a Samba DC,
 | 
						|
	   (b) trusted AD domains and we are joined to a non-kerberos domain
 | 
						|
	   (c) trusted AD domains and we are joined to a kerberos (AD) domain
 | 
						|
 | 
						|
	   For (a) we can always contact the trusted domain using krb5 
 | 
						|
	   since we have the domain trust account password
 | 
						|
 | 
						|
	   For (b) we can only use RPC since we have no way of 
 | 
						|
	   getting a krb5 ticket in our own domain
 | 
						|
 | 
						|
	   For (c) we can always use krb5 since we have a kerberos trust
 | 
						|
 | 
						|
	   --jerry
 | 
						|
	 */
 | 
						|
 | 
						|
	if (!domain->backend) {
 | 
						|
#ifdef HAVE_ADS
 | 
						|
		struct winbindd_domain *our_domain = domain;
 | 
						|
 | 
						|
		/* find our domain first so we can figure out if we 
 | 
						|
		   are joined to a kerberized domain */
 | 
						|
 | 
						|
		if ( !domain->primary )
 | 
						|
			our_domain = find_our_domain();
 | 
						|
 | 
						|
		if ((our_domain->active_directory || IS_DC)
 | 
						|
		    && domain->active_directory
 | 
						|
		    && !lp_winbind_rpc_only()) {
 | 
						|
			DEBUG(5,("get_cache: Setting ADS methods for domain %s\n", domain->name));
 | 
						|
			domain->backend = &ads_methods;
 | 
						|
		} else {
 | 
						|
#endif	/* HAVE_ADS */
 | 
						|
			DEBUG(5,("get_cache: Setting MS-RPC methods for domain %s\n", domain->name));
 | 
						|
			domain->backend = &reconnect_methods;
 | 
						|
#ifdef HAVE_ADS
 | 
						|
		}
 | 
						|
#endif	/* HAVE_ADS */
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret)
 | 
						|
		return ret;
 | 
						|
	
 | 
						|
	ret = SMB_XMALLOC_P(struct winbind_cache);
 | 
						|
	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);
 | 
						|
}
 | 
						|
 | 
						|
static bool centry_check_bytes(struct cache_entry *centry, size_t nbytes)
 | 
						|
{
 | 
						|
	if (centry->len - centry->ofs < nbytes) {
 | 
						|
		DEBUG(0,("centry corruption? needed %u bytes, have %d\n", 
 | 
						|
			 (unsigned int)nbytes,
 | 
						|
			 centry->len - centry->ofs));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  pull a uint32 from a cache entry 
 | 
						|
*/
 | 
						|
static uint32 centry_uint32(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	uint32 ret;
 | 
						|
 | 
						|
	if (!centry_check_bytes(centry, 4)) {
 | 
						|
		smb_panic_fn("centry_uint32");
 | 
						|
	}
 | 
						|
	ret = IVAL(centry->data, centry->ofs);
 | 
						|
	centry->ofs += 4;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  pull a uint16 from a cache entry 
 | 
						|
*/
 | 
						|
static uint16 centry_uint16(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	uint16 ret;
 | 
						|
	if (!centry_check_bytes(centry, 2)) {
 | 
						|
		smb_panic_fn("centry_uint16");
 | 
						|
	}
 | 
						|
	ret = CVAL(centry->data, centry->ofs);
 | 
						|
	centry->ofs += 2;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  pull a uint8 from a cache entry 
 | 
						|
*/
 | 
						|
static uint8 centry_uint8(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	uint8 ret;
 | 
						|
	if (!centry_check_bytes(centry, 1)) {
 | 
						|
		smb_panic_fn("centry_uint8");
 | 
						|
	}
 | 
						|
	ret = CVAL(centry->data, centry->ofs);
 | 
						|
	centry->ofs += 1;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  pull a NTTIME from a cache entry 
 | 
						|
*/
 | 
						|
static NTTIME centry_nttime(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	NTTIME ret;
 | 
						|
	if (!centry_check_bytes(centry, 8)) {
 | 
						|
		smb_panic_fn("centry_nttime");
 | 
						|
	}
 | 
						|
	ret = IVAL(centry->data, centry->ofs);
 | 
						|
	centry->ofs += 4;
 | 
						|
	ret += (uint64_t)IVAL(centry->data, centry->ofs) << 32;
 | 
						|
	centry->ofs += 4;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  pull a time_t from a cache entry. time_t stored portably as a 64-bit time.
 | 
						|
*/
 | 
						|
static time_t centry_time(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	return (time_t)centry_nttime(centry);
 | 
						|
}
 | 
						|
 | 
						|
/* 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_check_bytes(centry, (size_t)len)) {
 | 
						|
		smb_panic_fn("centry_string");
 | 
						|
	}
 | 
						|
 | 
						|
	ret = TALLOC_ARRAY(mem_ctx, char, len+1);
 | 
						|
	if (!ret) {
 | 
						|
		smb_panic_fn("centry_string out of memory\n");
 | 
						|
	}
 | 
						|
	memcpy(ret,centry->data + centry->ofs, len);
 | 
						|
	ret[len] = 0;
 | 
						|
	centry->ofs += len;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* pull a hash16 from a cache entry, using the supplied
 | 
						|
   talloc context 
 | 
						|
*/
 | 
						|
static char *centry_hash16(struct cache_entry *centry, TALLOC_CTX *mem_ctx)
 | 
						|
{
 | 
						|
	uint32 len;
 | 
						|
	char *ret;
 | 
						|
 | 
						|
	len = centry_uint8(centry);
 | 
						|
 | 
						|
	if (len != 16) {
 | 
						|
		DEBUG(0,("centry corruption? hash len (%u) != 16\n", 
 | 
						|
			len ));
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!centry_check_bytes(centry, 16)) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = TALLOC_ARRAY(mem_ctx, char, 16);
 | 
						|
	if (!ret) {
 | 
						|
		smb_panic_fn("centry_hash out of memory\n");
 | 
						|
	}
 | 
						|
	memcpy(ret,centry->data + centry->ofs, 16);
 | 
						|
	centry->ofs += 16;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* pull a sid from a cache entry, using the supplied
 | 
						|
   talloc context 
 | 
						|
*/
 | 
						|
static bool centry_sid(struct cache_entry *centry, TALLOC_CTX *mem_ctx, DOM_SID *sid)
 | 
						|
{
 | 
						|
	char *sid_string;
 | 
						|
	sid_string = centry_string(centry, mem_ctx);
 | 
						|
	if ((sid_string == NULL) || (!string_to_sid(sid, sid_string))) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  pull a NTSTATUS from a cache entry
 | 
						|
*/
 | 
						|
static NTSTATUS centry_ntstatus(struct cache_entry *centry)
 | 
						|
{
 | 
						|
	NTSTATUS status;
 | 
						|
 | 
						|
	status = NT_STATUS(centry_uint32(centry));
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 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);
 | 
						|
	
 | 
						|
	SAFE_FREE(data.dptr);
 | 
						|
 | 
						|
	/* 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;
 | 
						|
	fstring key_str;
 | 
						|
	uint8 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 );
 | 
						|
	
 | 
						|
	SIVAL(buf, 0, domain->sequence_number);
 | 
						|
	SIVAL(buf, 4, domain->last_seq_check);
 | 
						|
	data.dptr = buf;
 | 
						|
	data.dsize = 8;
 | 
						|
	
 | 
						|
	if ( tdb_store_bystring( wcache->tdb, key_str, 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();
 | 
						|
 | 
						|
	if ( IS_DOMAIN_OFFLINE(domain) ) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	
 | 
						|
	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));
 | 
						|
		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;	
 | 
						|
 | 
						|
	/* important! make sure that we know if this is a native 
 | 
						|
	   mode domain or not.  And that we can contact it. */
 | 
						|
 | 
						|
	if ( winbindd_can_contact_domain( domain ) ) {		
 | 
						|
		status = domain->backend->sequence_number(domain, 
 | 
						|
							  &domain->sequence_number);
 | 
						|
	} else {
 | 
						|
		/* just use the current time */
 | 
						|
		status = NT_STATUS_OK;
 | 
						|
		domain->sequence_number = time(NULL);
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/* the above call could have set our domain->backend to NULL when
 | 
						|
	 * coming from offline to online mode, make sure to reinitialize the
 | 
						|
	 * backend - Guenther */
 | 
						|
	get_cache( domain );
 | 
						|
 | 
						|
	if (!NT_STATUS_IS_OK(status)) {
 | 
						|
		DEBUG(10,("refresh_sequence_number: failed with %s\n", nt_errstr(status)));
 | 
						|
		domain->sequence_number = DOM_SEQUENCE_NONE;
 | 
						|
	}
 | 
						|
	
 | 
						|
	domain->last_status = status;
 | 
						|
	domain->last_seq_check = time(NULL);
 | 
						|
	
 | 
						|
	/* save the new sequence number in 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 we've been told to be offline - stay in that state... */
 | 
						|
	if (lp_winbind_offline_logon() && global_winbindd_offline_state) {
 | 
						|
		DEBUG(10,("centry_expired: Key %s for domain %s valid as winbindd is globally offline.\n",
 | 
						|
			keystr, domain->name ));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/* when the domain is offline return the cached entry.
 | 
						|
	 * This deals with transient offline states... */
 | 
						|
 | 
						|
	if (!domain->online) {
 | 
						|
		DEBUG(10,("centry_expired: Key %s for domain %s valid as domain is offline.\n",
 | 
						|
			keystr, domain->name ));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/* 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;
 | 
						|
}
 | 
						|
 | 
						|
static struct cache_entry *wcache_fetch_raw(char *kstr)
 | 
						|
{
 | 
						|
	TDB_DATA data;
 | 
						|
	struct cache_entry *centry;
 | 
						|
	TDB_DATA key;
 | 
						|
 | 
						|
	key = string_tdb_data(kstr);
 | 
						|
	data = tdb_fetch(wcache->tdb, key);
 | 
						|
	if (!data.dptr) {
 | 
						|
		/* a cache miss */
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = SMB_XMALLOC_P(struct cache_entry);
 | 
						|
	centry->data = (unsigned char *)data.dptr;
 | 
						|
	centry->len = data.dsize;
 | 
						|
	centry->ofs = 0;
 | 
						|
 | 
						|
	if (centry->len < 8) {
 | 
						|
		/* huh? corrupt cache? */
 | 
						|
		DEBUG(10,("wcache_fetch_raw: Corrupt cache for key %s (len < 8) ?\n", kstr));
 | 
						|
		centry_free(centry);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	
 | 
						|
	centry->status = centry_ntstatus(centry);
 | 
						|
	centry->sequence_number = centry_uint32(centry);
 | 
						|
 | 
						|
	return centry;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  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;
 | 
						|
	struct cache_entry *centry;
 | 
						|
 | 
						|
	if (opt_nocache) {
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
 | 
						|
	va_start(ap, format);
 | 
						|
	smb_xvasprintf(&kstr, format, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	centry = wcache_fetch_raw(kstr);
 | 
						|
	if (centry == NULL) {
 | 
						|
		free(kstr);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	if (centry_expired(domain, kstr, centry)) {
 | 
						|
 | 
						|
		DEBUG(10,("wcache_fetch: entry %s expired for domain %s\n",
 | 
						|
			 kstr, domain->name ));
 | 
						|
 | 
						|
		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;
 | 
						|
}
 | 
						|
 | 
						|
static void wcache_delete(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
 | 
						|
static void wcache_delete(const char *format, ...)
 | 
						|
{
 | 
						|
	va_list ap;
 | 
						|
	char *kstr;
 | 
						|
	TDB_DATA key;
 | 
						|
 | 
						|
	va_start(ap, format);
 | 
						|
	smb_xvasprintf(&kstr, format, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	key = string_tdb_data(kstr);
 | 
						|
 | 
						|
	tdb_delete(wcache->tdb, key);
 | 
						|
	free(kstr);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  make sure we have at least len bytes available in a centry 
 | 
						|
*/
 | 
						|
static void centry_expand(struct cache_entry *centry, uint32 len)
 | 
						|
{
 | 
						|
	if (centry->len - centry->ofs >= len)
 | 
						|
		return;
 | 
						|
	centry->len *= 2;
 | 
						|
	centry->data = SMB_REALLOC_ARRAY(centry->data, unsigned char,
 | 
						|
					 centry->len);
 | 
						|
	if (!centry->data) {
 | 
						|
		DEBUG(0,("out of memory: needed %d bytes in centry_expand\n", centry->len));
 | 
						|
		smb_panic_fn("out of memory in centry_expand");
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  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 uint16 into a centry 
 | 
						|
*/
 | 
						|
static void centry_put_uint16(struct cache_entry *centry, uint16 v)
 | 
						|
{
 | 
						|
	centry_expand(centry, 2);
 | 
						|
	SIVAL(centry->data, centry->ofs, v);
 | 
						|
	centry->ofs += 2;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  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) {
 | 
						|
		DEBUG(10,("centry_put_string: truncating len (%d) to: 254\n", len));
 | 
						|
		len = 254;
 | 
						|
	}
 | 
						|
	centry_put_uint8(centry, len);
 | 
						|
	centry_expand(centry, len);
 | 
						|
	memcpy(centry->data + centry->ofs, s, len);
 | 
						|
	centry->ofs += len;
 | 
						|
}
 | 
						|
 | 
						|
/* 
 | 
						|
   push a 16 byte hash into a centry - treat as 16 byte string.
 | 
						|
 */
 | 
						|
static void centry_put_hash16(struct cache_entry *centry, const uint8 val[16])
 | 
						|
{
 | 
						|
	centry_put_uint8(centry, 16);
 | 
						|
	centry_expand(centry, 16);
 | 
						|
	memcpy(centry->data + centry->ofs, val, 16);
 | 
						|
	centry->ofs += 16;
 | 
						|
}
 | 
						|
 | 
						|
static void centry_put_sid(struct cache_entry *centry, const DOM_SID *sid) 
 | 
						|
{
 | 
						|
	fstring sid_string;
 | 
						|
	centry_put_string(centry, sid_to_fstring(sid_string, sid));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  put NTSTATUS into a centry
 | 
						|
*/
 | 
						|
static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
 | 
						|
{
 | 
						|
	uint32 status_value = NT_STATUS_V(status);
 | 
						|
	centry_put_uint32(centry, status_value);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
  push a NTTIME into a centry 
 | 
						|
*/
 | 
						|
static void centry_put_nttime(struct cache_entry *centry, NTTIME nt)
 | 
						|
{
 | 
						|
	centry_expand(centry, 8);
 | 
						|
	SIVAL(centry->data, centry->ofs, nt & 0xFFFFFFFF);
 | 
						|
	centry->ofs += 4;
 | 
						|
	SIVAL(centry->data, centry->ofs, nt >> 32);
 | 
						|
	centry->ofs += 4;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  push a time_t into a centry - use a 64 bit size.
 | 
						|
  NTTIME here is being used as a convenient 64-bit size.
 | 
						|
*/
 | 
						|
static void centry_put_time(struct cache_entry *centry, time_t t)
 | 
						|
{
 | 
						|
	NTTIME nt = (NTTIME)t;
 | 
						|
	centry_put_nttime(centry, nt);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  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_P(struct cache_entry);
 | 
						|
 | 
						|
	centry->len = 8192; /* reasonable default */
 | 
						|
	centry->data = SMB_XMALLOC_ARRAY(uint8, centry->len);
 | 
						|
	centry->ofs = 0;
 | 
						|
	centry->sequence_number = domain->sequence_number;
 | 
						|
	centry_put_ntstatus(centry, 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;
 | 
						|
 | 
						|
	if (opt_nocache) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	va_start(ap, format);
 | 
						|
	smb_xvasprintf(&kstr, format, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	key = string_tdb_data(kstr);
 | 
						|
	data.dptr = 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 *domain_name,
 | 
						|
				    const char *name, const DOM_SID *sid, 
 | 
						|
				    enum lsa_SidType type)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
	fstring uname;
 | 
						|
 | 
						|
	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		return;
 | 
						|
	centry_put_uint32(centry, type);
 | 
						|
	centry_put_sid(centry, sid);
 | 
						|
	fstrcpy(uname, name);
 | 
						|
	strupper_m(uname);
 | 
						|
	centry_end(centry, "NS/%s/%s", domain_name, uname);
 | 
						|
	DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name,
 | 
						|
		  uname, sid_string_dbg(sid), nt_errstr(status)));
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status, 
 | 
						|
				    const DOM_SID *sid, const char *domain_name, const char *name, enum lsa_SidType 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, domain_name);
 | 
						|
		centry_put_string(centry, name);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_end(centry, "SN/%s", sid_to_fstring(sid_string, sid));
 | 
						|
	DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string, 
 | 
						|
		  name, nt_errstr(status)));
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status, WINBIND_USERINFO *info)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
	fstring sid_string;
 | 
						|
 | 
						|
	if (is_null_sid(&info->user_sid)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		return;
 | 
						|
	centry_put_string(centry, info->acct_name);
 | 
						|
	centry_put_string(centry, info->full_name);
 | 
						|
	centry_put_string(centry, info->homedir);
 | 
						|
	centry_put_string(centry, info->shell);
 | 
						|
	centry_put_uint32(centry, info->primary_gid);
 | 
						|
	centry_put_sid(centry, &info->user_sid);
 | 
						|
	centry_put_sid(centry, &info->group_sid);
 | 
						|
	centry_end(centry, "U/%s", sid_to_fstring(sid_string,
 | 
						|
						  &info->user_sid));
 | 
						|
	DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
static void wcache_save_lockout_policy(struct winbindd_domain *domain,
 | 
						|
				       NTSTATUS status,
 | 
						|
				       struct samr_DomInfo12 *lockout_policy)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
 | 
						|
	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		return;
 | 
						|
 | 
						|
	centry_put_nttime(centry, lockout_policy->lockout_duration);
 | 
						|
	centry_put_nttime(centry, lockout_policy->lockout_window);
 | 
						|
	centry_put_uint16(centry, lockout_policy->lockout_threshold);
 | 
						|
 | 
						|
	centry_end(centry, "LOC_POL/%s", domain->name);
 | 
						|
 | 
						|
	DEBUG(10,("wcache_save_lockout_policy: %s\n", domain->name));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
static void wcache_save_password_policy(struct winbindd_domain *domain,
 | 
						|
					NTSTATUS status,
 | 
						|
					struct samr_DomInfo1 *policy)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
 | 
						|
	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		return;
 | 
						|
 | 
						|
	centry_put_uint16(centry, policy->min_password_length);
 | 
						|
	centry_put_uint16(centry, policy->password_history_length);
 | 
						|
	centry_put_uint32(centry, policy->password_properties);
 | 
						|
	centry_put_nttime(centry, policy->max_password_age);
 | 
						|
	centry_put_nttime(centry, policy->min_password_age);
 | 
						|
 | 
						|
	centry_end(centry, "PWD_POL/%s", domain->name);
 | 
						|
 | 
						|
	DEBUG(10,("wcache_save_password_policy: %s\n", domain->name));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const DOM_SID *sid)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	TDB_DATA data;
 | 
						|
	fstring key_str, tmp;
 | 
						|
	uint32 rid;
 | 
						|
 | 
						|
	if (!cache->tdb) {
 | 
						|
		return NT_STATUS_INTERNAL_DB_ERROR;
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_null_sid(sid)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
 | 
						|
 | 
						|
	data = tdb_fetch(cache->tdb, string_tdb_data(key_str));
 | 
						|
	if (!data.dptr) {
 | 
						|
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 | 
						|
	}
 | 
						|
 | 
						|
	SAFE_FREE(data.dptr);
 | 
						|
	return NT_STATUS_OK;
 | 
						|
}
 | 
						|
 | 
						|
/* Lookup creds for a SID - copes with old (unsalted) creds as well
 | 
						|
   as new salted ones. */
 | 
						|
 | 
						|
NTSTATUS wcache_get_creds(struct winbindd_domain *domain, 
 | 
						|
			  TALLOC_CTX *mem_ctx, 
 | 
						|
			  const DOM_SID *sid,
 | 
						|
			  const uint8 **cached_nt_pass,
 | 
						|
			  const uint8 **cached_salt)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	time_t t;
 | 
						|
	uint32 rid;
 | 
						|
	fstring tmp;
 | 
						|
 | 
						|
	if (!cache->tdb) {
 | 
						|
		return NT_STATUS_INTERNAL_DB_ERROR;
 | 
						|
	}
 | 
						|
 | 
						|
	if (is_null_sid(sid)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Try and get a salted cred first. If we can't
 | 
						|
	   fall back to an unsalted cred. */
 | 
						|
 | 
						|
	centry = wcache_fetch(cache, domain, "CRED/%s",
 | 
						|
			      sid_to_fstring(tmp, sid));
 | 
						|
	if (!centry) {
 | 
						|
		DEBUG(10,("wcache_get_creds: entry for [CRED/%s] not found\n", 
 | 
						|
			  sid_string_dbg(sid)));
 | 
						|
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 | 
						|
	}
 | 
						|
 | 
						|
	t = centry_time(centry);
 | 
						|
 | 
						|
	/* In the salted case this isn't actually the nt_hash itself,
 | 
						|
	   but the MD5 of the salt + nt_hash. Let the caller
 | 
						|
	   sort this out. It can tell as we only return the cached_salt
 | 
						|
	   if we are returning a salted cred. */
 | 
						|
 | 
						|
	*cached_nt_pass = (const uint8 *)centry_hash16(centry, mem_ctx);
 | 
						|
	if (*cached_nt_pass == NULL) {
 | 
						|
		fstring sidstr;
 | 
						|
 | 
						|
		sid_to_fstring(sidstr, sid);
 | 
						|
 | 
						|
		/* Bad (old) cred cache. Delete and pretend we
 | 
						|
		   don't have it. */
 | 
						|
		DEBUG(0,("wcache_get_creds: bad entry for [CRED/%s] - deleting\n", 
 | 
						|
				sidstr));
 | 
						|
		wcache_delete("CRED/%s", sidstr);
 | 
						|
		centry_free(centry);
 | 
						|
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 | 
						|
	}
 | 
						|
 | 
						|
	/* We only have 17 bytes more data in the salted cred case. */
 | 
						|
	if (centry->len - centry->ofs == 17) {
 | 
						|
		*cached_salt = (const uint8 *)centry_hash16(centry, mem_ctx);
 | 
						|
	} else {
 | 
						|
		*cached_salt = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	dump_data_pw("cached_nt_pass", *cached_nt_pass, NT_HASH_LEN);
 | 
						|
	if (*cached_salt) {
 | 
						|
		dump_data_pw("cached_salt", *cached_salt, NT_HASH_LEN);
 | 
						|
	}
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
 | 
						|
		  sid_string_dbg(sid), nt_errstr(status) ));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
/* Store creds for a SID - only writes out new salted ones. */
 | 
						|
 | 
						|
NTSTATUS wcache_save_creds(struct winbindd_domain *domain, 
 | 
						|
			   TALLOC_CTX *mem_ctx, 
 | 
						|
			   const DOM_SID *sid, 
 | 
						|
			   const uint8 nt_pass[NT_HASH_LEN])
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
	fstring sid_string;
 | 
						|
	uint32 rid;
 | 
						|
	uint8 cred_salt[NT_HASH_LEN];
 | 
						|
	uint8 salted_hash[NT_HASH_LEN];
 | 
						|
 | 
						|
	if (is_null_sid(sid)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!(sid_peek_rid(sid, &rid)) || (rid == 0)) {
 | 
						|
		return NT_STATUS_INVALID_SID;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = centry_start(domain, NT_STATUS_OK);
 | 
						|
	if (!centry) {
 | 
						|
		return NT_STATUS_INTERNAL_DB_ERROR;
 | 
						|
	}
 | 
						|
 | 
						|
	dump_data_pw("nt_pass", nt_pass, NT_HASH_LEN);
 | 
						|
 | 
						|
	centry_put_time(centry, time(NULL));
 | 
						|
 | 
						|
	/* Create a salt and then salt the hash. */
 | 
						|
	generate_random_buffer(cred_salt, NT_HASH_LEN);
 | 
						|
	E_md5hash(cred_salt, nt_pass, salted_hash);
 | 
						|
 | 
						|
	centry_put_hash16(centry, salted_hash);
 | 
						|
	centry_put_hash16(centry, cred_salt);
 | 
						|
	centry_end(centry, "CRED/%s", sid_to_fstring(sid_string, sid));
 | 
						|
 | 
						|
	DEBUG(10,("wcache_save_creds: %s\n", sid_string));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	return NT_STATUS_OK;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 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_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
 | 
						|
	if (! (*info)) {
 | 
						|
		smb_panic_fn("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].homedir = centry_string(centry, mem_ctx);
 | 
						|
		(*info)[i].shell = centry_string(centry, mem_ctx);
 | 
						|
		centry_sid(centry, mem_ctx, &(*info)[i].user_sid);
 | 
						|
		centry_sid(centry, mem_ctx, &(*info)[i].group_sid);
 | 
						|
	}
 | 
						|
 | 
						|
do_cached:	
 | 
						|
	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("query_user_list: [Cached] - cached list for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(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_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
 | 
						|
			DEBUG(3, ("query_user_list: flushing "
 | 
						|
				  "connection cache\n"));
 | 
						|
			invalidate_cm_connection(&domain->conn);
 | 
						|
		}
 | 
						|
 | 
						|
	} 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_string(centry, (*info)[i].homedir);
 | 
						|
		centry_put_string(centry, (*info)[i].shell);
 | 
						|
		centry_put_sid(centry, &(*info)[i].user_sid);
 | 
						|
		centry_put_sid(centry, &(*info)[i].group_sid);
 | 
						|
		if (domain->backend && domain->backend->consistent) {
 | 
						|
			/* when the backend is consistent we can pre-prime some mappings */
 | 
						|
			wcache_save_name_to_sid(domain, NT_STATUS_OK, 
 | 
						|
						domain->name,
 | 
						|
						(*info)[i].acct_name, 
 | 
						|
						&(*info)[i].user_sid,
 | 
						|
						SID_NAME_USER);
 | 
						|
			wcache_save_sid_to_name(domain, NT_STATUS_OK, 
 | 
						|
						&(*info)[i].user_sid,
 | 
						|
						domain->name,
 | 
						|
						(*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_ARRAY(mem_ctx, struct acct_info, *num_entries);
 | 
						|
	if (! (*info)) {
 | 
						|
		smb_panic_fn("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, nt_errstr(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_ARRAY(mem_ctx, struct acct_info, *num_entries);
 | 
						|
	if (! (*info)) {
 | 
						|
		smb_panic_fn("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, nt_errstr(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,
 | 
						|
			    enum winbindd_cmd orig_cmd,
 | 
						|
			    const char *domain_name,
 | 
						|
			    const char *name,
 | 
						|
			    DOM_SID *sid,
 | 
						|
			    enum lsa_SidType *type)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	fstring uname;
 | 
						|
 | 
						|
	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;
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
	if (NT_STATUS_IS_OK(status)) {
 | 
						|
		*type = (enum lsa_SidType)centry_uint32(centry);
 | 
						|
		centry_sid(centry, mem_ctx, sid);
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,("name_to_sid: [Cached] - cached name for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(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, orig_cmd, 
 | 
						|
					      domain_name, name, sid, type);
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
 | 
						|
	if (domain->online &&
 | 
						|
	    (NT_STATUS_IS_OK(status) || NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))) {
 | 
						|
		wcache_save_name_to_sid(domain, status, domain_name, name, sid, *type);
 | 
						|
	
 | 
						|
	/* Don't add SN cache entries for sid-to-name queries during this operation.
 | 
						|
	 * It leads to inconsistent answers during sid-to-name queries as the
 | 
						|
	 * client can ask for different combinations of lower case and upper case
 | 
						|
	 * names in these name-to-sid queries.
 | 
						|
	 */
 | 
						|
 | 
						|
	}
 | 
						|
	
 | 
						|
	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 **domain_name,
 | 
						|
			    char **name,
 | 
						|
			    enum lsa_SidType *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_fstring(sid_string, sid));
 | 
						|
	if (!centry)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
	if (NT_STATUS_IS_OK(status)) {
 | 
						|
		*type = (enum lsa_SidType)centry_uint32(centry);
 | 
						|
		*domain_name = centry_string(centry, mem_ctx);
 | 
						|
		*name = centry_string(centry, mem_ctx);
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,("sid_to_name: [Cached] - cached name for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(status) ));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
	return status;
 | 
						|
 | 
						|
do_query:
 | 
						|
	*name = NULL;
 | 
						|
	*domain_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, domain_name, name, type);
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
	wcache_save_sid_to_name(domain, status, sid, *domain_name, *name, *type);
 | 
						|
 | 
						|
	/* We can't save the name to sid mapping here, as with sid history a
 | 
						|
	 * later name2sid would give the wrong sid. */
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 | 
						|
			      TALLOC_CTX *mem_ctx,
 | 
						|
			      const DOM_SID *domain_sid,
 | 
						|
			      uint32 *rids,
 | 
						|
			      size_t num_rids,
 | 
						|
			      char **domain_name,
 | 
						|
			      char ***names,
 | 
						|
			      enum lsa_SidType **types)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	size_t i;
 | 
						|
	NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
 | 
						|
	bool have_mapped;
 | 
						|
	bool have_unmapped;
 | 
						|
 | 
						|
	*domain_name = NULL;
 | 
						|
	*names = NULL;
 | 
						|
	*types = NULL;
 | 
						|
 | 
						|
	if (!cache->tdb) {
 | 
						|
		goto do_query;
 | 
						|
	}
 | 
						|
 | 
						|
	if (num_rids == 0) {
 | 
						|
		return NT_STATUS_OK;
 | 
						|
	}
 | 
						|
 | 
						|
	*names = TALLOC_ARRAY(mem_ctx, char *, num_rids);
 | 
						|
	*types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids);
 | 
						|
 | 
						|
	if ((*names == NULL) || (*types == NULL)) {
 | 
						|
		result = NT_STATUS_NO_MEMORY;
 | 
						|
		goto error;
 | 
						|
	}
 | 
						|
 | 
						|
	have_mapped = have_unmapped = false;
 | 
						|
 | 
						|
	for (i=0; i<num_rids; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		struct cache_entry *centry;
 | 
						|
		fstring tmp;
 | 
						|
 | 
						|
		if (!sid_compose(&sid, domain_sid, rids[i])) {
 | 
						|
			result = NT_STATUS_INTERNAL_ERROR;
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		centry = wcache_fetch(cache, domain, "SN/%s",
 | 
						|
				      sid_to_fstring(tmp, &sid));
 | 
						|
		if (!centry) {
 | 
						|
			goto do_query;
 | 
						|
		}
 | 
						|
 | 
						|
		(*types)[i] = SID_NAME_UNKNOWN;
 | 
						|
		(*names)[i] = talloc_strdup(*names, "");
 | 
						|
 | 
						|
		if (NT_STATUS_IS_OK(centry->status)) {
 | 
						|
			char *dom;
 | 
						|
			have_mapped = true;
 | 
						|
			(*types)[i] = (enum lsa_SidType)centry_uint32(centry);
 | 
						|
 | 
						|
			dom = centry_string(centry, mem_ctx);
 | 
						|
			if (*domain_name == NULL) {
 | 
						|
				*domain_name = dom;
 | 
						|
			} else {
 | 
						|
				talloc_free(dom);
 | 
						|
			}
 | 
						|
 | 
						|
			(*names)[i] = centry_string(centry, *names);
 | 
						|
 | 
						|
		} else if (NT_STATUS_EQUAL(centry->status, NT_STATUS_NONE_MAPPED)) {
 | 
						|
			have_unmapped = true;
 | 
						|
 | 
						|
		} else {
 | 
						|
			/* something's definitely wrong */
 | 
						|
			result = centry->status;
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		centry_free(centry);
 | 
						|
	}
 | 
						|
 | 
						|
	if (!have_mapped) {
 | 
						|
		return NT_STATUS_NONE_MAPPED;
 | 
						|
	}
 | 
						|
	if (!have_unmapped) {
 | 
						|
		return NT_STATUS_OK;
 | 
						|
	}
 | 
						|
	return STATUS_SOME_UNMAPPED;
 | 
						|
 | 
						|
 do_query:
 | 
						|
 | 
						|
	TALLOC_FREE(*names);
 | 
						|
	TALLOC_FREE(*types);
 | 
						|
 | 
						|
	result = domain->backend->rids_to_names(domain, mem_ctx, domain_sid,
 | 
						|
						rids, num_rids, domain_name,
 | 
						|
						names, types);
 | 
						|
 | 
						|
	/*
 | 
						|
	  None of the queried rids has been found so save all negative entries
 | 
						|
	*/
 | 
						|
	if (NT_STATUS_EQUAL(result, NT_STATUS_NONE_MAPPED)) {
 | 
						|
		for (i = 0; i < num_rids; i++) {
 | 
						|
			DOM_SID sid;
 | 
						|
			const char *name = "";
 | 
						|
			const enum lsa_SidType type = SID_NAME_UNKNOWN;
 | 
						|
			NTSTATUS status = NT_STATUS_NONE_MAPPED;
 | 
						|
			
 | 
						|
			if (!sid_compose(&sid, domain_sid, rids[i])) {
 | 
						|
				return NT_STATUS_INTERNAL_ERROR;
 | 
						|
			}
 | 
						|
 | 
						|
			wcache_save_sid_to_name(domain, status, &sid, *domain_name,
 | 
						|
						name, type);
 | 
						|
		}
 | 
						|
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	  Some or all of the queried rids have been found.
 | 
						|
	*/
 | 
						|
	if (!NT_STATUS_IS_OK(result) &&
 | 
						|
	    !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
 | 
						|
	for (i=0; i<num_rids; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		NTSTATUS status;
 | 
						|
 | 
						|
		if (!sid_compose(&sid, domain_sid, rids[i])) {
 | 
						|
			result = NT_STATUS_INTERNAL_ERROR;
 | 
						|
			goto error;
 | 
						|
		}
 | 
						|
 | 
						|
		status = (*types)[i] == SID_NAME_UNKNOWN ?
 | 
						|
			NT_STATUS_NONE_MAPPED : NT_STATUS_OK;
 | 
						|
 | 
						|
		wcache_save_sid_to_name(domain, status, &sid, *domain_name,
 | 
						|
					(*names)[i], (*types)[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	return result;
 | 
						|
 | 
						|
 error:
 | 
						|
	
 | 
						|
	TALLOC_FREE(*names);
 | 
						|
	TALLOC_FREE(*types);
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
/* Lookup user information from a rid */
 | 
						|
static NTSTATUS query_user(struct winbindd_domain *domain, 
 | 
						|
			   TALLOC_CTX *mem_ctx, 
 | 
						|
			   const DOM_SID *user_sid, 
 | 
						|
			   WINBIND_USERINFO *info)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	fstring tmp;
 | 
						|
 | 
						|
	if (!cache->tdb)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	centry = wcache_fetch(cache, domain, "U/%s",
 | 
						|
			      sid_to_fstring(tmp, 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;
 | 
						|
	
 | 
						|
	/* if status is not ok then this is a negative hit
 | 
						|
	   and the rest of the data doesn't matter */
 | 
						|
	status = centry->status;
 | 
						|
	if (NT_STATUS_IS_OK(status)) {
 | 
						|
		info->acct_name = centry_string(centry, mem_ctx);
 | 
						|
		info->full_name = centry_string(centry, mem_ctx);
 | 
						|
		info->homedir = centry_string(centry, mem_ctx);
 | 
						|
		info->shell = centry_string(centry, mem_ctx);
 | 
						|
		info->primary_gid = centry_uint32(centry);
 | 
						|
		centry_sid(centry, mem_ctx, &info->user_sid);
 | 
						|
		centry_sid(centry, mem_ctx, &info->group_sid);
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,("query_user: [Cached] - cached info for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(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,("query_user: [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,
 | 
						|
				  const 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_fstring(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, ("lookup_usergroups: 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_ARRAY(mem_ctx, DOM_SID, *num_groups);
 | 
						|
	if (! (*user_gids)) {
 | 
						|
		smb_panic_fn("lookup_usergroups out of memory");
 | 
						|
	}
 | 
						|
	for (i=0; i<(*num_groups); i++) {
 | 
						|
		centry_sid(centry, mem_ctx, &(*user_gids)[i]);
 | 
						|
	}
 | 
						|
 | 
						|
do_cached:	
 | 
						|
	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("lookup_usergroups: [Cached] - cached info for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(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);
 | 
						|
 | 
						|
	if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
 | 
						|
		goto skip_save;
 | 
						|
	
 | 
						|
	/* 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_fstring(sid_string, user_sid));
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
skip_save:
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
static NTSTATUS lookup_useraliases(struct winbindd_domain *domain,
 | 
						|
				   TALLOC_CTX *mem_ctx,
 | 
						|
				   uint32 num_sids, const DOM_SID *sids,
 | 
						|
				   uint32 *num_aliases, uint32 **alias_rids)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	char *sidlist = talloc_strdup(mem_ctx, "");
 | 
						|
	int i;
 | 
						|
 | 
						|
	if (!cache->tdb)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	if (num_sids == 0) {
 | 
						|
		*num_aliases = 0;
 | 
						|
		*alias_rids = NULL;
 | 
						|
		return NT_STATUS_OK;
 | 
						|
	}
 | 
						|
 | 
						|
	/* We need to cache indexed by the whole list of SIDs, the aliases
 | 
						|
	 * resulting might come from any of the SIDs. */
 | 
						|
 | 
						|
	for (i=0; i<num_sids; i++) {
 | 
						|
		fstring tmp;
 | 
						|
		sidlist = talloc_asprintf(mem_ctx, "%s/%s", sidlist,
 | 
						|
					  sid_to_fstring(tmp, &sids[i]));
 | 
						|
		if (sidlist == NULL)
 | 
						|
			return NT_STATUS_NO_MEMORY;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = wcache_fetch(cache, domain, "UA%s", sidlist);
 | 
						|
 | 
						|
	if (!centry)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	*num_aliases = centry_uint32(centry);
 | 
						|
	*alias_rids = NULL;
 | 
						|
 | 
						|
	if (*num_aliases) {
 | 
						|
		(*alias_rids) = TALLOC_ARRAY(mem_ctx, uint32, *num_aliases);
 | 
						|
 | 
						|
		if ((*alias_rids) == NULL) {
 | 
						|
			centry_free(centry);
 | 
						|
			return NT_STATUS_NO_MEMORY;
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		(*alias_rids) = NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i=0; i<(*num_aliases); i++)
 | 
						|
		(*alias_rids)[i] = centry_uint32(centry);
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("lookup_useraliases: [Cached] - cached info for domain: %s "
 | 
						|
		  "status %s\n", domain->name, nt_errstr(status)));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
	return status;
 | 
						|
 | 
						|
 do_query:
 | 
						|
	(*num_aliases) = 0;
 | 
						|
	(*alias_rids) = NULL;
 | 
						|
 | 
						|
	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_useraliases(domain, mem_ctx,
 | 
						|
						     num_sids, sids,
 | 
						|
						     num_aliases, alias_rids);
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		goto skip_save;
 | 
						|
	centry_put_uint32(centry, *num_aliases);
 | 
						|
	for (i=0; i<(*num_aliases); i++)
 | 
						|
		centry_put_uint32(centry, (*alias_rids)[i]);
 | 
						|
	centry_end(centry, "UA%s", sidlist);
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
 skip_save:
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static NTSTATUS lookup_groupmem(struct winbindd_domain *domain,
 | 
						|
				TALLOC_CTX *mem_ctx,
 | 
						|
				const 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_fstring(sid_string, group_sid));
 | 
						|
	if (!centry)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	*num_names = centry_uint32(centry);
 | 
						|
	
 | 
						|
	if (*num_names == 0)
 | 
						|
		goto do_cached;
 | 
						|
 | 
						|
	(*sid_mem) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_names);
 | 
						|
	(*names) = TALLOC_ARRAY(mem_ctx, char *, *num_names);
 | 
						|
	(*name_types) = TALLOC_ARRAY(mem_ctx, uint32, *num_names);
 | 
						|
 | 
						|
	if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
 | 
						|
		smb_panic_fn("lookup_groupmem out of memory");
 | 
						|
	}
 | 
						|
 | 
						|
	for (i=0; i<(*num_names); i++) {
 | 
						|
		centry_sid(centry, mem_ctx, &(*sid_mem)[i]);
 | 
						|
		(*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, nt_errstr(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_fstring(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 
 | 
						|
 * (we need to have the list of trustdoms in the cache when we go offline) -
 | 
						|
 * Guenther */
 | 
						|
static NTSTATUS trusted_domains(struct winbindd_domain *domain,
 | 
						|
				TALLOC_CTX *mem_ctx,
 | 
						|
				uint32 *num_domains,
 | 
						|
				char ***names,
 | 
						|
				char ***alt_names,
 | 
						|
				DOM_SID **dom_sids)
 | 
						|
{
 | 
						|
 	struct winbind_cache *cache = get_cache(domain);
 | 
						|
 	struct cache_entry *centry = NULL;
 | 
						|
 	NTSTATUS status;
 | 
						|
	int i;
 | 
						|
 
 | 
						|
	if (!cache->tdb)
 | 
						|
		goto do_query;
 | 
						|
 
 | 
						|
	centry = wcache_fetch(cache, domain, "TRUSTDOMS/%s", domain->name);
 | 
						|
	
 | 
						|
	if (!centry) {
 | 
						|
 		goto do_query;
 | 
						|
	}
 | 
						|
 
 | 
						|
	*num_domains = centry_uint32(centry);
 | 
						|
	
 | 
						|
	if (*num_domains) {
 | 
						|
		(*names) 	= TALLOC_ARRAY(mem_ctx, char *, *num_domains);
 | 
						|
		(*alt_names) 	= TALLOC_ARRAY(mem_ctx, char *, *num_domains);
 | 
						|
		(*dom_sids) 	= TALLOC_ARRAY(mem_ctx, DOM_SID, *num_domains);
 | 
						|
 
 | 
						|
		if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
 | 
						|
			smb_panic_fn("trusted_domains out of memory");
 | 
						|
 		}
 | 
						|
	} else {
 | 
						|
		(*names) = NULL;
 | 
						|
		(*alt_names) = NULL;
 | 
						|
		(*dom_sids) = NULL;
 | 
						|
	}
 | 
						|
 
 | 
						|
	for (i=0; i<(*num_domains); i++) {
 | 
						|
		(*names)[i] = centry_string(centry, mem_ctx);
 | 
						|
		(*alt_names)[i] = centry_string(centry, mem_ctx);
 | 
						|
		centry_sid(centry, mem_ctx, &(*dom_sids)[i]);
 | 
						|
	}
 | 
						|
 | 
						|
 	status = centry->status;
 | 
						|
 
 | 
						|
	DEBUG(10,("trusted_domains: [Cached] - cached info for domain %s (%d trusts) status: %s\n",
 | 
						|
		domain->name, *num_domains, nt_errstr(status) ));
 | 
						|
 
 | 
						|
 	centry_free(centry);
 | 
						|
 	return status;
 | 
						|
 
 | 
						|
do_query:
 | 
						|
	(*num_domains) = 0;
 | 
						|
	(*dom_sids) = NULL;
 | 
						|
	(*names) = NULL;
 | 
						|
	(*alt_names) = NULL;
 | 
						|
 
 | 
						|
	/* Return status value returned by seq number check */
 | 
						|
 | 
						|
 	if (!NT_STATUS_IS_OK(domain->last_status))
 | 
						|
 		return domain->last_status;
 | 
						|
	
 | 
						|
	DEBUG(10,("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
 | 
						|
		domain->name ));
 | 
						|
 
 | 
						|
	status = domain->backend->trusted_domains(domain, mem_ctx, num_domains,
 | 
						|
						names, alt_names, dom_sids);
 | 
						|
 | 
						|
	/* no trusts gives NT_STATUS_NO_MORE_ENTRIES resetting to NT_STATUS_OK
 | 
						|
	 * so that the generic centry handling still applies correctly -
 | 
						|
	 * Guenther*/
 | 
						|
 | 
						|
	if (!NT_STATUS_IS_ERR(status)) {
 | 
						|
		status = NT_STATUS_OK;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
#if 0    /* Disabled as we want the trust dom list to be managed by
 | 
						|
	    the main parent and always to make the query.  --jerry */
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
 
 | 
						|
 	centry = centry_start(domain, status);
 | 
						|
	if (!centry)
 | 
						|
		goto skip_save;
 | 
						|
 | 
						|
	centry_put_uint32(centry, *num_domains);
 | 
						|
 | 
						|
	for (i=0; i<(*num_domains); i++) {
 | 
						|
		centry_put_string(centry, (*names)[i]);
 | 
						|
		centry_put_string(centry, (*alt_names)[i]);
 | 
						|
		centry_put_sid(centry, &(*dom_sids)[i]);
 | 
						|
 	}
 | 
						|
	
 | 
						|
	centry_end(centry, "TRUSTDOMS/%s", domain->name);
 | 
						|
 
 | 
						|
 	centry_free(centry);
 | 
						|
 
 | 
						|
skip_save:
 | 
						|
#endif
 | 
						|
 | 
						|
 	return status;
 | 
						|
}	
 | 
						|
 | 
						|
/* get lockout policy */
 | 
						|
static NTSTATUS lockout_policy(struct winbindd_domain *domain,
 | 
						|
 			       TALLOC_CTX *mem_ctx,
 | 
						|
			       struct samr_DomInfo12 *policy)
 | 
						|
{
 | 
						|
 	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, "LOC_POL/%s", domain->name);
 | 
						|
 | 
						|
	if (!centry)
 | 
						|
 		goto do_query;
 | 
						|
 | 
						|
	policy->lockout_duration = centry_nttime(centry);
 | 
						|
	policy->lockout_window = centry_nttime(centry);
 | 
						|
	policy->lockout_threshold = centry_uint16(centry);
 | 
						|
 | 
						|
 	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(status) ));
 | 
						|
 | 
						|
 	centry_free(centry);
 | 
						|
 	return status;
 | 
						|
 | 
						|
do_query:
 | 
						|
	ZERO_STRUCTP(policy);
 | 
						|
 | 
						|
	/* Return status value returned by seq number check */
 | 
						|
 | 
						|
 	if (!NT_STATUS_IS_OK(domain->last_status))
 | 
						|
 		return domain->last_status;
 | 
						|
 | 
						|
	DEBUG(10,("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
 | 
						|
		domain->name ));
 | 
						|
 | 
						|
	status = domain->backend->lockout_policy(domain, mem_ctx, policy);
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
 	refresh_sequence_number(domain, false);
 | 
						|
	wcache_save_lockout_policy(domain, status, policy);
 | 
						|
 | 
						|
 	return status;
 | 
						|
}
 | 
						|
 | 
						|
/* get password policy */
 | 
						|
static NTSTATUS password_policy(struct winbindd_domain *domain,
 | 
						|
				TALLOC_CTX *mem_ctx,
 | 
						|
				struct samr_DomInfo1 *policy)
 | 
						|
{
 | 
						|
	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, "PWD_POL/%s", domain->name);
 | 
						|
 | 
						|
	if (!centry)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	policy->min_password_length = centry_uint16(centry);
 | 
						|
	policy->password_history_length = centry_uint16(centry);
 | 
						|
	policy->password_properties = centry_uint32(centry);
 | 
						|
	policy->max_password_age = centry_nttime(centry);
 | 
						|
	policy->min_password_age = centry_nttime(centry);
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
 | 
						|
	DEBUG(10,("lockout_policy: [Cached] - cached info for domain %s status: %s\n",
 | 
						|
		domain->name, nt_errstr(status) ));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
	return status;
 | 
						|
 | 
						|
do_query:
 | 
						|
	ZERO_STRUCTP(policy);
 | 
						|
 | 
						|
	/* Return status value returned by seq number check */
 | 
						|
 | 
						|
	if (!NT_STATUS_IS_OK(domain->last_status))
 | 
						|
		return domain->last_status;
 | 
						|
 | 
						|
	DEBUG(10,("password_policy: [Cached] - doing backend query for info for domain %s\n",
 | 
						|
		domain->name ));
 | 
						|
 | 
						|
	status = domain->backend->password_policy(domain, mem_ctx, policy);
 | 
						|
 | 
						|
	/* and save it */
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
	if (NT_STATUS_IS_OK(status)) {
 | 
						|
		wcache_save_password_policy(domain, status, policy);
 | 
						|
	}
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 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((const char *)kbuf.dptr, "UL/", 3) == 0 ||
 | 
						|
	    strncmp((const char *)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, 
 | 
						|
				struct netr_SamInfo3 *info3)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache;
 | 
						|
 | 
						|
	/* dont clear cached U/SID and UG/SID entries when we want to logon
 | 
						|
	 * offline - gd */
 | 
						|
 | 
						|
	if (lp_winbind_offline_logon()) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!domain)
 | 
						|
		return;
 | 
						|
 | 
						|
	cache = get_cache(domain);
 | 
						|
	netsamlogon_clear_cached_user(cache->tdb, info3);
 | 
						|
}
 | 
						|
 | 
						|
bool 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) {
 | 
						|
			if (cache->tdb) {
 | 
						|
				tdb_traverse(cache->tdb, traverse_fn, NULL);
 | 
						|
			} else {
 | 
						|
				return false;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
bool init_wcache(void)
 | 
						|
{
 | 
						|
	if (wcache == NULL) {
 | 
						|
		wcache = SMB_XMALLOC_P(struct winbind_cache);
 | 
						|
		ZERO_STRUCTP(wcache);
 | 
						|
	}
 | 
						|
 | 
						|
	if (wcache->tdb != NULL)
 | 
						|
		return true;
 | 
						|
 | 
						|
	/* when working offline we must not clear the cache on restart */
 | 
						|
	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
 | 
						|
				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
 | 
						|
				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
 | 
						|
				O_RDWR|O_CREAT, 0600);
 | 
						|
 | 
						|
	if (wcache->tdb == NULL) {
 | 
						|
		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/************************************************************************
 | 
						|
 This is called by the parent to initialize the cache file.
 | 
						|
 We don't need sophisticated locking here as we know we're the
 | 
						|
 only opener.
 | 
						|
************************************************************************/
 | 
						|
 | 
						|
bool initialize_winbindd_cache(void)
 | 
						|
{
 | 
						|
	bool cache_bad = true;
 | 
						|
	uint32 vers;
 | 
						|
 | 
						|
	if (!init_wcache()) {
 | 
						|
		DEBUG(0,("initialize_winbindd_cache: init_wcache failed.\n"));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Check version number. */
 | 
						|
	if (tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers) &&
 | 
						|
			vers == WINBINDD_CACHE_VERSION) {
 | 
						|
		cache_bad = false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (cache_bad) {
 | 
						|
		DEBUG(0,("initialize_winbindd_cache: clearing cache "
 | 
						|
			"and re-creating with version number %d\n",
 | 
						|
			WINBINDD_CACHE_VERSION ));
 | 
						|
 | 
						|
		tdb_close(wcache->tdb);
 | 
						|
		wcache->tdb = NULL;
 | 
						|
 | 
						|
		if (unlink(lock_path("winbindd_cache.tdb")) == -1) {
 | 
						|
			DEBUG(0,("initialize_winbindd_cache: unlink %s failed %s ",
 | 
						|
				lock_path("winbindd_cache.tdb"),
 | 
						|
				strerror(errno) ));
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
		if (!init_wcache()) {
 | 
						|
			DEBUG(0,("initialize_winbindd_cache: re-initialization "
 | 
						|
					"init_wcache failed.\n"));
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Write the version. */
 | 
						|
		if (!tdb_store_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION)) {
 | 
						|
			DEBUG(0,("initialize_winbindd_cache: version number store failed %s\n",
 | 
						|
				tdb_errorstr(wcache->tdb) ));
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	tdb_close(wcache->tdb);
 | 
						|
	wcache->tdb = NULL;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void close_winbindd_cache(void)
 | 
						|
{
 | 
						|
	if (!wcache) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (wcache->tdb) {
 | 
						|
		tdb_close(wcache->tdb);
 | 
						|
		wcache->tdb = NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void cache_store_response(pid_t pid, struct winbindd_response *response)
 | 
						|
{
 | 
						|
	fstring key_str;
 | 
						|
 | 
						|
	if (!init_wcache())
 | 
						|
		return;
 | 
						|
 | 
						|
	DEBUG(10, ("Storing response for pid %d, len %d\n",
 | 
						|
		   pid, response->length));
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DR/%d", pid);
 | 
						|
	if (tdb_store(wcache->tdb, string_tdb_data(key_str), 
 | 
						|
		      make_tdb_data((uint8 *)response, sizeof(*response)),
 | 
						|
		      TDB_REPLACE) == -1)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (response->length == sizeof(*response))
 | 
						|
		return;
 | 
						|
 | 
						|
	/* There's extra data */
 | 
						|
 | 
						|
	DEBUG(10, ("Storing extra data: len=%d\n",
 | 
						|
		   (int)(response->length - sizeof(*response))));
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DE/%d", pid);
 | 
						|
	if (tdb_store(wcache->tdb, string_tdb_data(key_str),
 | 
						|
		      make_tdb_data((uint8 *)response->extra_data.data,
 | 
						|
				    response->length - sizeof(*response)),
 | 
						|
		      TDB_REPLACE) == 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* We could not store the extra data, make sure the tdb does not
 | 
						|
	 * contain a main record with wrong dangling extra data */
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DR/%d", pid);
 | 
						|
	tdb_delete(wcache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
bool cache_retrieve_response(pid_t pid, struct winbindd_response * response)
 | 
						|
{
 | 
						|
	TDB_DATA data;
 | 
						|
	fstring key_str;
 | 
						|
 | 
						|
	if (!init_wcache())
 | 
						|
		return false;
 | 
						|
 | 
						|
	DEBUG(10, ("Retrieving response for pid %d\n", pid));
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DR/%d", pid);
 | 
						|
	data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
	if (data.dptr == NULL)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (data.dsize != sizeof(*response))
 | 
						|
		return false;
 | 
						|
 | 
						|
	memcpy(response, data.dptr, data.dsize);
 | 
						|
	SAFE_FREE(data.dptr);
 | 
						|
 | 
						|
	if (response->length == sizeof(*response)) {
 | 
						|
		response->extra_data.data = NULL;
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	/* There's extra data */
 | 
						|
 | 
						|
	DEBUG(10, ("Retrieving extra data length=%d\n",
 | 
						|
		   (int)(response->length - sizeof(*response))));
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DE/%d", pid);
 | 
						|
	data = tdb_fetch(wcache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
	if (data.dptr == NULL) {
 | 
						|
		DEBUG(0, ("Did not find extra data\n"));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (data.dsize != (response->length - sizeof(*response))) {
 | 
						|
		DEBUG(0, ("Invalid extra data length: %d\n", (int)data.dsize));
 | 
						|
		SAFE_FREE(data.dptr);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	dump_data(11, (uint8 *)data.dptr, data.dsize);
 | 
						|
 | 
						|
	response->extra_data.data = data.dptr;
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void cache_cleanup_response(pid_t pid)
 | 
						|
{
 | 
						|
	fstring key_str;
 | 
						|
 | 
						|
	if (!init_wcache())
 | 
						|
		return;
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DR/%d", pid);
 | 
						|
	tdb_delete(wcache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
	fstr_sprintf(key_str, "DE/%d", pid);
 | 
						|
	tdb_delete(wcache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
	return;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid,
 | 
						|
		       char **domain_name, char **name,
 | 
						|
		       enum lsa_SidType *type)
 | 
						|
{
 | 
						|
	struct winbindd_domain *domain;
 | 
						|
	struct winbind_cache *cache;
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	fstring tmp;
 | 
						|
 | 
						|
	domain = find_lookup_domain_from_sid(sid);
 | 
						|
	if (domain == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	cache = get_cache(domain);
 | 
						|
 | 
						|
	if (cache->tdb == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = wcache_fetch(cache, domain, "SN/%s",
 | 
						|
			      sid_to_fstring(tmp, sid));
 | 
						|
	if (centry == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (NT_STATUS_IS_OK(centry->status)) {
 | 
						|
		*type = (enum lsa_SidType)centry_uint32(centry);
 | 
						|
		*domain_name = centry_string(centry, mem_ctx);
 | 
						|
		*name = centry_string(centry, mem_ctx);
 | 
						|
	}
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
	centry_free(centry);
 | 
						|
	return NT_STATUS_IS_OK(status);
 | 
						|
}
 | 
						|
 | 
						|
bool lookup_cached_name(TALLOC_CTX *mem_ctx,
 | 
						|
			const char *domain_name,
 | 
						|
			const char *name,
 | 
						|
			DOM_SID *sid,
 | 
						|
			enum lsa_SidType *type)
 | 
						|
{
 | 
						|
	struct winbindd_domain *domain;
 | 
						|
	struct winbind_cache *cache;
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS status;
 | 
						|
	fstring uname;
 | 
						|
	bool original_online_state;	
 | 
						|
 | 
						|
	domain = find_lookup_domain_from_name(domain_name);
 | 
						|
	if (domain == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	cache = get_cache(domain);
 | 
						|
 | 
						|
	if (cache->tdb == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	fstrcpy(uname, name);
 | 
						|
	strupper_m(uname);
 | 
						|
	
 | 
						|
	/* If we are doing a cached logon, temporarily set the domain
 | 
						|
	   offline so the cache won't expire the entry */
 | 
						|
	
 | 
						|
	original_online_state = domain->online;
 | 
						|
	domain->online = false;
 | 
						|
	centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
 | 
						|
	domain->online = original_online_state;
 | 
						|
	
 | 
						|
	if (centry == NULL) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (NT_STATUS_IS_OK(centry->status)) {
 | 
						|
		*type = (enum lsa_SidType)centry_uint32(centry);
 | 
						|
		centry_sid(centry, mem_ctx, sid);
 | 
						|
	}
 | 
						|
 | 
						|
	status = centry->status;
 | 
						|
	centry_free(centry);
 | 
						|
	
 | 
						|
	return NT_STATUS_IS_OK(status);
 | 
						|
}
 | 
						|
 | 
						|
void cache_name2sid(struct winbindd_domain *domain, 
 | 
						|
		    const char *domain_name, const char *name,
 | 
						|
		    enum lsa_SidType type, const DOM_SID *sid)
 | 
						|
{
 | 
						|
	refresh_sequence_number(domain, false);
 | 
						|
	wcache_save_name_to_sid(domain, NT_STATUS_OK, domain_name, name,
 | 
						|
				sid, type);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * The original idea that this cache only contains centries has
 | 
						|
 * been blurred - now other stuff gets put in here. Ensure we
 | 
						|
 * ignore these things on cleanup.
 | 
						|
 */
 | 
						|
 | 
						|
static int traverse_fn_cleanup(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, 
 | 
						|
			       TDB_DATA dbuf, void *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
 | 
						|
	if (is_non_centry_key(kbuf)) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	centry = wcache_fetch_raw((char *)kbuf.dptr);
 | 
						|
	if (!centry) {
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!NT_STATUS_IS_OK(centry->status)) {
 | 
						|
		DEBUG(10,("deleting centry %s\n", (const char *)kbuf.dptr));
 | 
						|
		tdb_delete(the_tdb, kbuf);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* flush the cache */
 | 
						|
void wcache_flush_cache(void)
 | 
						|
{
 | 
						|
	if (!wcache)
 | 
						|
		return;
 | 
						|
	if (wcache->tdb) {
 | 
						|
		tdb_close(wcache->tdb);
 | 
						|
		wcache->tdb = NULL;
 | 
						|
	}
 | 
						|
	if (opt_nocache)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* when working offline we must not clear the cache on restart */
 | 
						|
	wcache->tdb = tdb_open_log(lock_path("winbindd_cache.tdb"),
 | 
						|
				WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, 
 | 
						|
				lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), 
 | 
						|
				O_RDWR|O_CREAT, 0600);
 | 
						|
 | 
						|
	if (!wcache->tdb) {
 | 
						|
		DEBUG(0,("Failed to open winbindd_cache.tdb!\n"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
 | 
						|
 | 
						|
	DEBUG(10,("wcache_flush_cache success\n"));
 | 
						|
}
 | 
						|
 | 
						|
/* Count cached creds */
 | 
						|
 | 
						|
static int traverse_fn_cached_creds(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
 | 
						|
			 	    void *state)
 | 
						|
{
 | 
						|
	int *cred_count = (int*)state;
 | 
						|
 
 | 
						|
	if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
 | 
						|
		(*cred_count)++;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
NTSTATUS wcache_count_cached_creds(struct winbindd_domain *domain, int *count)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
 | 
						|
	*count = 0;
 | 
						|
 | 
						|
	if (!cache->tdb) {
 | 
						|
		return NT_STATUS_INTERNAL_DB_ERROR;
 | 
						|
	}
 | 
						|
 
 | 
						|
	tdb_traverse(cache->tdb, traverse_fn_cached_creds, (void *)count);
 | 
						|
 | 
						|
	return NT_STATUS_OK;
 | 
						|
}
 | 
						|
 | 
						|
struct cred_list {
 | 
						|
	struct cred_list *prev, *next;
 | 
						|
	TDB_DATA key;
 | 
						|
	fstring name;
 | 
						|
	time_t created;
 | 
						|
};
 | 
						|
static struct cred_list *wcache_cred_list;
 | 
						|
 | 
						|
static int traverse_fn_get_credlist(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, 
 | 
						|
				    void *state)
 | 
						|
{
 | 
						|
	struct cred_list *cred;
 | 
						|
 | 
						|
	if (strncmp((const char *)kbuf.dptr, "CRED/", 5) == 0) {
 | 
						|
 | 
						|
		cred = SMB_MALLOC_P(struct cred_list);
 | 
						|
		if (cred == NULL) {
 | 
						|
			DEBUG(0,("traverse_fn_remove_first_creds: failed to malloc new entry for list\n"));
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		ZERO_STRUCTP(cred);
 | 
						|
		
 | 
						|
		/* save a copy of the key */
 | 
						|
		
 | 
						|
		fstrcpy(cred->name, (const char *)kbuf.dptr);		
 | 
						|
		DLIST_ADD(wcache_cred_list, cred);
 | 
						|
	}
 | 
						|
	
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
NTSTATUS wcache_remove_oldest_cached_creds(struct winbindd_domain *domain, const DOM_SID *sid) 
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	NTSTATUS status;
 | 
						|
	int ret;
 | 
						|
	struct cred_list *cred, *oldest = NULL;
 | 
						|
 | 
						|
	if (!cache->tdb) {
 | 
						|
		return NT_STATUS_INTERNAL_DB_ERROR;
 | 
						|
	}
 | 
						|
 | 
						|
	/* we possibly already have an entry */
 | 
						|
 	if (sid && NT_STATUS_IS_OK(wcache_cached_creds_exist(domain, sid))) {
 | 
						|
	
 | 
						|
		fstring key_str, tmp;
 | 
						|
 | 
						|
		DEBUG(11,("we already have an entry, deleting that\n"));
 | 
						|
 | 
						|
		fstr_sprintf(key_str, "CRED/%s", sid_to_fstring(tmp, sid));
 | 
						|
 | 
						|
		tdb_delete(cache->tdb, string_tdb_data(key_str));
 | 
						|
 | 
						|
		return NT_STATUS_OK;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = tdb_traverse(cache->tdb, traverse_fn_get_credlist, NULL);
 | 
						|
	if (ret == 0) {
 | 
						|
		return NT_STATUS_OK;
 | 
						|
	} else if ((ret == -1) || (wcache_cred_list == NULL)) {
 | 
						|
		return NT_STATUS_OBJECT_NAME_NOT_FOUND;
 | 
						|
	}
 | 
						|
 | 
						|
	ZERO_STRUCTP(oldest);
 | 
						|
 | 
						|
	for (cred = wcache_cred_list; cred; cred = cred->next) {
 | 
						|
 | 
						|
		TDB_DATA data;
 | 
						|
		time_t t;
 | 
						|
 | 
						|
		data = tdb_fetch(cache->tdb, string_tdb_data(cred->name));
 | 
						|
		if (!data.dptr) {
 | 
						|
			DEBUG(10,("wcache_remove_oldest_cached_creds: entry for [%s] not found\n", 
 | 
						|
				cred->name));
 | 
						|
			status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
 | 
						|
			goto done;
 | 
						|
		}
 | 
						|
	
 | 
						|
		t = IVAL(data.dptr, 0);
 | 
						|
		SAFE_FREE(data.dptr);
 | 
						|
 | 
						|
		if (!oldest) {
 | 
						|
			oldest = SMB_MALLOC_P(struct cred_list);
 | 
						|
			if (oldest == NULL) {
 | 
						|
				status = NT_STATUS_NO_MEMORY;
 | 
						|
				goto done;
 | 
						|
			}
 | 
						|
 | 
						|
			fstrcpy(oldest->name, cred->name);
 | 
						|
			oldest->created = t;
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
 | 
						|
		if (t < oldest->created) {
 | 
						|
			fstrcpy(oldest->name, cred->name);
 | 
						|
			oldest->created = t;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (tdb_delete(cache->tdb, string_tdb_data(oldest->name)) == 0) {
 | 
						|
		status = NT_STATUS_OK;
 | 
						|
	} else {
 | 
						|
		status = NT_STATUS_UNSUCCESSFUL;
 | 
						|
	}
 | 
						|
done:
 | 
						|
	SAFE_FREE(wcache_cred_list);
 | 
						|
	SAFE_FREE(oldest);
 | 
						|
	
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
/* Change the global online/offline state. */
 | 
						|
bool set_global_winbindd_state_offline(void)
 | 
						|
{
 | 
						|
	TDB_DATA data;
 | 
						|
 | 
						|
	DEBUG(10,("set_global_winbindd_state_offline: offline requested.\n"));
 | 
						|
 | 
						|
	/* Only go offline if someone has created
 | 
						|
	   the key "WINBINDD_OFFLINE" in the cache tdb. */
 | 
						|
 | 
						|
	if (wcache == NULL || wcache->tdb == NULL) {
 | 
						|
		DEBUG(10,("set_global_winbindd_state_offline: wcache not open yet.\n"));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!lp_winbind_offline_logon()) {
 | 
						|
		DEBUG(10,("set_global_winbindd_state_offline: rejecting.\n"));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	if (global_winbindd_offline_state) {
 | 
						|
		/* Already offline. */
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
 | 
						|
	data = tdb_fetch_bystring( wcache->tdb, "WINBINDD_OFFLINE" );
 | 
						|
 | 
						|
	if (!data.dptr || data.dsize != 4) {
 | 
						|
		DEBUG(10,("set_global_winbindd_state_offline: offline state not set.\n"));
 | 
						|
		SAFE_FREE(data.dptr);
 | 
						|
		return false;
 | 
						|
	} else {
 | 
						|
		DEBUG(10,("set_global_winbindd_state_offline: offline state set.\n"));
 | 
						|
		global_winbindd_offline_state = true;
 | 
						|
		SAFE_FREE(data.dptr);
 | 
						|
		return true;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void set_global_winbindd_state_online(void)
 | 
						|
{
 | 
						|
	DEBUG(10,("set_global_winbindd_state_online: online requested.\n"));
 | 
						|
 | 
						|
	if (!lp_winbind_offline_logon()) {
 | 
						|
		DEBUG(10,("set_global_winbindd_state_online: rejecting.\n"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (!global_winbindd_offline_state) {
 | 
						|
		/* Already online. */
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	global_winbindd_offline_state = false;
 | 
						|
 | 
						|
	if (!wcache->tdb) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Ensure there is no key "WINBINDD_OFFLINE" in the cache tdb. */
 | 
						|
	tdb_delete_bystring(wcache->tdb, "WINBINDD_OFFLINE");
 | 
						|
}
 | 
						|
 | 
						|
bool get_global_winbindd_state_offline(void)
 | 
						|
{
 | 
						|
	return global_winbindd_offline_state;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 Validate functions for all possible cache tdb keys.
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data, 
 | 
						|
						  struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
 | 
						|
	centry = SMB_XMALLOC_P(struct cache_entry);
 | 
						|
	centry->data = (unsigned char *)memdup(data.dptr, data.dsize);
 | 
						|
	if (!centry->data) {
 | 
						|
		SAFE_FREE(centry);
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
	centry->len = data.dsize;
 | 
						|
	centry->ofs = 0;
 | 
						|
 | 
						|
	if (centry->len < 8) {
 | 
						|
		/* huh? corrupt cache? */
 | 
						|
		DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
 | 
						|
		centry_free(centry);
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	centry->status = NT_STATUS(centry_uint32(centry));
 | 
						|
	centry->sequence_number = centry_uint32(centry);
 | 
						|
	return centry;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			   struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	if (dbuf.dsize != 8) {
 | 
						|
		DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
 | 
						|
				keystr, (unsigned int)dbuf.dsize ));
 | 
						|
		state->bad_entry = true;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_uint32(centry);
 | 
						|
	if (NT_STATUS_IS_OK(centry->status)) {
 | 
						|
		DOM_SID sid;
 | 
						|
		(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_ns: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	if (NT_STATUS_IS_OK(centry->status)) {
 | 
						|
		(void)centry_uint32(centry);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_sn: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		      struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	DOM_SID sid;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_uint32(centry);
 | 
						|
	(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
	(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_u: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			    struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_nttime(centry);
 | 
						|
	(void)centry_nttime(centry);
 | 
						|
	(void)centry_uint16(centry);
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			    struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_uint16(centry);
 | 
						|
	(void)centry_uint16(centry);
 | 
						|
	(void)centry_uint32(centry);
 | 
						|
	(void)centry_nttime(centry);
 | 
						|
	(void)centry_nttime(centry);
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			 struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_time(centry);
 | 
						|
	(void)centry_hash16(centry, mem_ctx);
 | 
						|
 | 
						|
	/* We only have 17 bytes more data in the salted cred case. */
 | 
						|
	if (centry->len - centry->ofs == 17) {
 | 
						|
		(void)centry_hash16(centry, mem_ctx);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_cred: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_entries, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_entries = (int32)centry_uint32(centry);
 | 
						|
 | 
						|
	for (i=0; i< num_entries; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
		(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_ul: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_entries, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_entries = centry_uint32(centry);
 | 
						|
	
 | 
						|
	for (i=0; i< num_entries; i++) {
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_uint32(centry);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_gl: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_groups, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_groups = centry_uint32(centry);
 | 
						|
 | 
						|
	for (i=0; i< num_groups; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		centry_sid(centry, mem_ctx, &sid);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_ug: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_aliases, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_aliases = centry_uint32(centry);
 | 
						|
 | 
						|
	for (i=0; i < num_aliases; i++) {
 | 
						|
		(void)centry_uint32(centry);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_ua: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_names, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_names = centry_uint32(centry);
 | 
						|
 | 
						|
	for (i=0; i< num_names; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		centry_sid(centry, mem_ctx, &sid);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_uint32(centry);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_gm: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	/* Can't say anything about this other than must be nonzero. */
 | 
						|
	if (dbuf.dsize == 0) {
 | 
						|
		DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
 | 
						|
				keystr));
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,("validate_dr: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
		       struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	/* Can't say anything about this other than must be nonzero. */
 | 
						|
	if (dbuf.dsize == 0) {
 | 
						|
		DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
 | 
						|
				keystr));
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,("validate_de: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_pwinfo(TALLOC_CTX *mem_ctx, const char *keystr,
 | 
						|
			   TDB_DATA dbuf, struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_string(centry, mem_ctx);
 | 
						|
	(void)centry_uint32(centry);
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_pwinfo: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			      struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
 | 
						|
	int32 num_domains, i;
 | 
						|
 | 
						|
	if (!centry) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	num_domains = centry_uint32(centry);
 | 
						|
	
 | 
						|
	for (i=0; i< num_domains; i++) {
 | 
						|
		DOM_SID sid;
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_string(centry, mem_ctx);
 | 
						|
		(void)centry_sid(centry, mem_ctx, &sid);
 | 
						|
	}
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	if (!(state->success)) {
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr, 
 | 
						|
				  TDB_DATA dbuf,
 | 
						|
				  struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	if (dbuf.dsize == 0) {
 | 
						|
		DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
 | 
						|
			  "key %s (len ==0) ?\n", keystr));
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10,    ("validate_trustdomcache: %s ok\n", keystr));
 | 
						|
	DEBUGADD(10, ("  Don't trust me, I am a DUMMY!\n"));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
			    struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	if (dbuf.dsize != 4) {
 | 
						|
		DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
 | 
						|
				keystr, (unsigned int)dbuf.dsize ));
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	DEBUG(10,("validate_offline: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
 | 
						|
				  struct tdb_validation_status *state)
 | 
						|
{
 | 
						|
	if (dbuf.dsize != 4) {
 | 
						|
		DEBUG(0, ("validate_cache_version: Corrupt cache for "
 | 
						|
			  "key %s (len %u != 4) ?\n", 
 | 
						|
			  keystr, (unsigned int)dbuf.dsize));
 | 
						|
		state->bad_entry = true;
 | 
						|
		state->success = false;
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 A list of all possible cache tdb keys with associated validation
 | 
						|
 functions.
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
struct key_val_struct {
 | 
						|
	const char *keyname;
 | 
						|
	int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct tdb_validation_status* state);
 | 
						|
} key_val[] = {
 | 
						|
	{"SEQNUM/", validate_seqnum},
 | 
						|
	{"NS/", validate_ns},
 | 
						|
	{"SN/", validate_sn},
 | 
						|
	{"U/", validate_u},
 | 
						|
	{"LOC_POL/", validate_loc_pol},
 | 
						|
	{"PWD_POL/", validate_pwd_pol},
 | 
						|
	{"CRED/", validate_cred},
 | 
						|
	{"UL/", validate_ul},
 | 
						|
	{"GL/", validate_gl},
 | 
						|
	{"UG/", validate_ug},
 | 
						|
	{"UA", validate_ua},
 | 
						|
	{"GM/", validate_gm},
 | 
						|
	{"DR/", validate_dr},
 | 
						|
	{"DE/", validate_de},
 | 
						|
	{"NSS/PWINFO/", validate_pwinfo},
 | 
						|
	{"TRUSTDOMS/", validate_trustdoms},
 | 
						|
	{"TRUSTDOMCACHE/", validate_trustdomcache},
 | 
						|
	{"WINBINDD_OFFLINE", validate_offline},
 | 
						|
	{WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
 | 
						|
	{NULL, NULL}
 | 
						|
};
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 Function to look at every entry in the tdb and validate it as far as
 | 
						|
 possible.
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	unsigned int max_key_len = 1024;
 | 
						|
	struct tdb_validation_status *v_state = (struct tdb_validation_status *)state;
 | 
						|
 | 
						|
	/* Paranoia check. */
 | 
						|
	if (strncmp("UA/", (const char *)kbuf.dptr, 3) == 0) {
 | 
						|
		max_key_len = 1024 * 1024;
 | 
						|
	}
 | 
						|
	if (kbuf.dsize > max_key_len) {
 | 
						|
		DEBUG(0, ("cache_traverse_validate_fn: key length too large: "
 | 
						|
			  "(%u) > (%u)\n\n",
 | 
						|
			  (unsigned int)kbuf.dsize, (unsigned int)max_key_len));
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; key_val[i].keyname; i++) {
 | 
						|
		size_t namelen = strlen(key_val[i].keyname);
 | 
						|
		if (kbuf.dsize >= namelen && (
 | 
						|
				strncmp(key_val[i].keyname, (const char *)kbuf.dptr, namelen)) == 0) {
 | 
						|
			TALLOC_CTX *mem_ctx;
 | 
						|
			char *keystr;
 | 
						|
			int ret;
 | 
						|
 | 
						|
			keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
 | 
						|
			if (!keystr) {
 | 
						|
				return 1;
 | 
						|
			}
 | 
						|
			memcpy(keystr, kbuf.dptr, kbuf.dsize);
 | 
						|
			keystr[kbuf.dsize] = '\0';
 | 
						|
 | 
						|
			mem_ctx = talloc_init("validate_ctx");
 | 
						|
			if (!mem_ctx) {
 | 
						|
				SAFE_FREE(keystr);
 | 
						|
				return 1;
 | 
						|
			}
 | 
						|
 | 
						|
			ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf, 
 | 
						|
							  v_state);
 | 
						|
 | 
						|
			SAFE_FREE(keystr);
 | 
						|
			talloc_destroy(mem_ctx);
 | 
						|
			return ret;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n"));
 | 
						|
	dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
 | 
						|
	DEBUG(0,("data :\n"));
 | 
						|
	dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
 | 
						|
	v_state->unknown_key = true;
 | 
						|
	v_state->success = false;
 | 
						|
	return 1; /* terminate. */
 | 
						|
}
 | 
						|
 | 
						|
static void validate_panic(const char *const why)
 | 
						|
{
 | 
						|
        DEBUG(0,("validating cache: would panic %s\n", why ));
 | 
						|
	DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
 | 
						|
	exit(47);
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 Try and validate every entry in the winbindd cache. If we fail here,
 | 
						|
 delete the cache tdb and return non-zero.
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
int winbindd_validate_cache(void)
 | 
						|
{
 | 
						|
	int ret = -1;
 | 
						|
	const char *tdb_path = lock_path("winbindd_cache.tdb");
 | 
						|
	TDB_CONTEXT *tdb = NULL;
 | 
						|
 | 
						|
	DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
 | 
						|
	smb_panic_fn = validate_panic;
 | 
						|
 | 
						|
 | 
						|
	tdb = tdb_open_log(tdb_path, 
 | 
						|
			   WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
 | 
						|
			   ( lp_winbind_offline_logon() 
 | 
						|
			     ? TDB_DEFAULT 
 | 
						|
			     : TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
 | 
						|
			   O_RDWR|O_CREAT, 
 | 
						|
			   0600);
 | 
						|
	if (!tdb) {
 | 
						|
		DEBUG(0, ("winbindd_validate_cache: "
 | 
						|
			  "error opening/initializing tdb\n"));
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
	tdb_close(tdb);
 | 
						|
 | 
						|
	ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
 | 
						|
 | 
						|
	if (ret != 0) {
 | 
						|
		DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
 | 
						|
		DEBUGADD(10, ("removing tdb %s.\n", tdb_path));
 | 
						|
		unlink(tdb_path);
 | 
						|
	}
 | 
						|
 | 
						|
done:
 | 
						|
	DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
 | 
						|
	smb_panic_fn = smb_panic;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
/***********************************************************************
 | 
						|
 Try and validate every entry in the winbindd cache.
 | 
						|
***********************************************************************/
 | 
						|
 | 
						|
int winbindd_validate_cache_nobackup(void)
 | 
						|
{
 | 
						|
	int ret = -1;
 | 
						|
	const char *tdb_path = lock_path("winbindd_cache.tdb");
 | 
						|
 | 
						|
	DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
 | 
						|
	smb_panic_fn = validate_panic;
 | 
						|
 | 
						|
 | 
						|
	if (wcache == NULL || wcache->tdb == NULL) {
 | 
						|
		ret = tdb_validate_open(tdb_path, cache_traverse_validate_fn);
 | 
						|
	} else {
 | 
						|
		ret = tdb_validate(wcache->tdb, cache_traverse_validate_fn);
 | 
						|
	}
 | 
						|
 | 
						|
	if (ret != 0) {
 | 
						|
		DEBUG(10, ("winbindd_validate_cache_nobackup: validation not "
 | 
						|
			   "successful.\n"));
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG(10, ("winbindd_validate_cache_nobackup: restoring panic "
 | 
						|
		   "function\n"));
 | 
						|
	smb_panic_fn = smb_panic;
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
bool winbindd_cache_validate_and_initialize(void)
 | 
						|
{
 | 
						|
	close_winbindd_cache();
 | 
						|
 | 
						|
	if (lp_winbind_offline_logon()) {
 | 
						|
		if (winbindd_validate_cache() < 0) {
 | 
						|
			DEBUG(0, ("winbindd cache tdb corrupt and no backup "
 | 
						|
				  "could be restored.\n"));
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return initialize_winbindd_cache();
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static bool add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
 | 
						|
				       struct winbindd_tdc_domain **domains, 
 | 
						|
				       size_t *num_domains )
 | 
						|
{
 | 
						|
	struct winbindd_tdc_domain *list = NULL;
 | 
						|
	size_t idx;
 | 
						|
	int i;
 | 
						|
	bool set_only = false;
 | 
						|
	
 | 
						|
	/* don't allow duplicates */
 | 
						|
 | 
						|
	idx = *num_domains;
 | 
						|
	list = *domains;
 | 
						|
	
 | 
						|
	for ( i=0; i< (*num_domains); i++ ) {
 | 
						|
		if ( strequal( new_dom->name, list[i].domain_name ) ) {
 | 
						|
			DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
 | 
						|
				  new_dom->name));
 | 
						|
			idx = i;
 | 
						|
			set_only = true;
 | 
						|
			
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if ( !set_only ) {
 | 
						|
		if ( !*domains ) {
 | 
						|
			list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
 | 
						|
			idx = 0;
 | 
						|
		} else {
 | 
						|
			list = TALLOC_REALLOC_ARRAY( *domains, *domains, 
 | 
						|
						     struct winbindd_tdc_domain,  
 | 
						|
						     (*num_domains)+1);
 | 
						|
			idx = *num_domains;		
 | 
						|
		}
 | 
						|
 | 
						|
		ZERO_STRUCT( list[idx] );
 | 
						|
	}
 | 
						|
 | 
						|
	if ( !list )
 | 
						|
		return false;
 | 
						|
 | 
						|
	list[idx].domain_name = talloc_strdup( list, new_dom->name );
 | 
						|
	list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
 | 
						|
 | 
						|
	if ( !is_null_sid( &new_dom->sid ) )
 | 
						|
		sid_copy( &list[idx].sid, &new_dom->sid );
 | 
						|
 | 
						|
	if ( new_dom->domain_flags != 0x0 )
 | 
						|
		list[idx].trust_flags = new_dom->domain_flags;	
 | 
						|
 | 
						|
	if ( new_dom->domain_type != 0x0 )
 | 
						|
		list[idx].trust_type = new_dom->domain_type;
 | 
						|
 | 
						|
	if ( new_dom->domain_trust_attribs != 0x0 )
 | 
						|
		list[idx].trust_attribs = new_dom->domain_trust_attribs;
 | 
						|
	
 | 
						|
	if ( !set_only ) {
 | 
						|
		*domains = list;
 | 
						|
		*num_domains = idx + 1;	
 | 
						|
	}
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static TDB_DATA make_tdc_key( const char *domain_name )
 | 
						|
{
 | 
						|
	char *keystr = NULL;
 | 
						|
	TDB_DATA key = { NULL, 0 };
 | 
						|
	
 | 
						|
	if ( !domain_name ) {
 | 
						|
		DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
 | 
						|
		return key;
 | 
						|
	}
 | 
						|
	       
 | 
						|
		
 | 
						|
	asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
 | 
						|
	key = string_term_tdb_data(keystr);
 | 
						|
	
 | 
						|
	return key;	
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static int pack_tdc_domains( struct winbindd_tdc_domain *domains, 
 | 
						|
			     size_t num_domains,
 | 
						|
			     unsigned char **buf )
 | 
						|
{
 | 
						|
        unsigned char *buffer = NULL;
 | 
						|
	int len = 0;
 | 
						|
	int buflen = 0;
 | 
						|
	int i = 0;
 | 
						|
 | 
						|
	DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
 | 
						|
		  (int)num_domains));
 | 
						|
	
 | 
						|
	buflen = 0;
 | 
						|
	
 | 
						|
 again: 
 | 
						|
	len = 0;
 | 
						|
	
 | 
						|
	/* Store the number of array items first */
 | 
						|
	len += tdb_pack( buffer+len, buflen-len, "d", 
 | 
						|
			 num_domains );
 | 
						|
 | 
						|
	/* now pack each domain trust record */
 | 
						|
	for ( i=0; i<num_domains; i++ ) {
 | 
						|
 | 
						|
		fstring tmp;
 | 
						|
 | 
						|
		if ( buflen > 0 ) {
 | 
						|
			DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
 | 
						|
				  domains[i].domain_name,
 | 
						|
				  domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
 | 
						|
		}
 | 
						|
		
 | 
						|
		len += tdb_pack( buffer+len, buflen-len, "fffddd",
 | 
						|
				 domains[i].domain_name,
 | 
						|
				 domains[i].dns_name,
 | 
						|
				 sid_to_fstring(tmp, &domains[i].sid),
 | 
						|
				 domains[i].trust_flags,
 | 
						|
				 domains[i].trust_attribs,
 | 
						|
				 domains[i].trust_type );
 | 
						|
	}
 | 
						|
 | 
						|
	if ( buflen < len ) {
 | 
						|
		SAFE_FREE(buffer);
 | 
						|
		if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
 | 
						|
			DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
 | 
						|
			buflen = -1;
 | 
						|
			goto done;
 | 
						|
		}
 | 
						|
		buflen = len;
 | 
						|
		goto again;
 | 
						|
	}
 | 
						|
 | 
						|
	*buf = buffer;	
 | 
						|
	
 | 
						|
 done:	
 | 
						|
	return buflen;	
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static size_t unpack_tdc_domains( unsigned char *buf, int buflen, 
 | 
						|
				  struct winbindd_tdc_domain **domains )
 | 
						|
{
 | 
						|
	fstring domain_name, dns_name, sid_string;	
 | 
						|
	uint32 type, attribs, flags;
 | 
						|
	int num_domains;
 | 
						|
	int len = 0;
 | 
						|
	int i;
 | 
						|
	struct winbindd_tdc_domain *list = NULL;
 | 
						|
 | 
						|
	/* get the number of domains */
 | 
						|
	len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
 | 
						|
	if ( len == -1 ) {
 | 
						|
		DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));		
 | 
						|
		return 0;
 | 
						|
	}
 | 
						|
 | 
						|
	list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
 | 
						|
	if ( !list ) {
 | 
						|
		DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
 | 
						|
		return 0;		
 | 
						|
	}
 | 
						|
	
 | 
						|
	for ( i=0; i<num_domains; i++ ) {
 | 
						|
		len += tdb_unpack( buf+len, buflen-len, "fffddd",
 | 
						|
				   domain_name,
 | 
						|
				   dns_name,
 | 
						|
				   sid_string,
 | 
						|
				   &flags,
 | 
						|
				   &attribs,
 | 
						|
				   &type );
 | 
						|
 | 
						|
		if ( len == -1 ) {
 | 
						|
			DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
 | 
						|
			TALLOC_FREE( list );			
 | 
						|
			return 0;
 | 
						|
		}
 | 
						|
 | 
						|
		DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
 | 
						|
			  "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
 | 
						|
			  domain_name, dns_name, sid_string,
 | 
						|
			  flags, attribs, type));
 | 
						|
		
 | 
						|
		list[i].domain_name = talloc_strdup( list, domain_name );
 | 
						|
		list[i].dns_name = talloc_strdup( list, dns_name );
 | 
						|
		if ( !string_to_sid( &(list[i].sid), sid_string ) ) {			
 | 
						|
			DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
 | 
						|
				  domain_name));
 | 
						|
		}
 | 
						|
		list[i].trust_flags = flags;
 | 
						|
		list[i].trust_attribs = attribs;
 | 
						|
		list[i].trust_type = type;
 | 
						|
	}
 | 
						|
 | 
						|
	*domains = list;
 | 
						|
	
 | 
						|
	return num_domains;
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static bool wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
 | 
						|
{
 | 
						|
	TDB_DATA key = make_tdc_key( lp_workgroup() );	 
 | 
						|
	TDB_DATA data = { NULL, 0 };
 | 
						|
	int ret;
 | 
						|
	
 | 
						|
	if ( !key.dptr )
 | 
						|
		return false;
 | 
						|
	
 | 
						|
	/* See if we were asked to delete the cache entry */
 | 
						|
 | 
						|
	if ( !domains ) {
 | 
						|
		ret = tdb_delete( wcache->tdb, key );
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
	
 | 
						|
	data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
 | 
						|
	
 | 
						|
	if ( !data.dptr ) {
 | 
						|
		ret = -1;
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
		
 | 
						|
	ret = tdb_store( wcache->tdb, key, data, 0 );
 | 
						|
 | 
						|
 done:
 | 
						|
	SAFE_FREE( data.dptr );
 | 
						|
	SAFE_FREE( key.dptr );
 | 
						|
	
 | 
						|
	return ( ret != -1 );	
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
bool wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
 | 
						|
{
 | 
						|
	TDB_DATA key = make_tdc_key( lp_workgroup() );
 | 
						|
	TDB_DATA data = { NULL, 0 };
 | 
						|
 | 
						|
	*domains = NULL;	
 | 
						|
	*num_domains = 0;	
 | 
						|
 | 
						|
	if ( !key.dptr )
 | 
						|
		return false;
 | 
						|
	
 | 
						|
	data = tdb_fetch( wcache->tdb, key );
 | 
						|
 | 
						|
	SAFE_FREE( key.dptr );
 | 
						|
	
 | 
						|
	if ( !data.dptr ) 
 | 
						|
		return false;
 | 
						|
	
 | 
						|
	*num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
 | 
						|
 | 
						|
	SAFE_FREE( data.dptr );
 | 
						|
	
 | 
						|
	if ( !*domains )
 | 
						|
		return false;
 | 
						|
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
bool wcache_tdc_add_domain( struct winbindd_domain *domain )
 | 
						|
{
 | 
						|
	struct winbindd_tdc_domain *dom_list = NULL;
 | 
						|
	size_t num_domains = 0;
 | 
						|
	bool ret = false;
 | 
						|
 | 
						|
	DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
 | 
						|
		  "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
 | 
						|
		  domain->name, domain->alt_name, 
 | 
						|
		  sid_string_dbg(&domain->sid),
 | 
						|
		  domain->domain_flags,
 | 
						|
		  domain->domain_trust_attribs,
 | 
						|
		  domain->domain_type));	
 | 
						|
	
 | 
						|
	if ( !init_wcache() ) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	
 | 
						|
	/* fetch the list */
 | 
						|
 | 
						|
	wcache_tdc_fetch_list( &dom_list, &num_domains );
 | 
						|
	
 | 
						|
	/* add the new domain */
 | 
						|
 | 
						|
	if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
 | 
						|
		goto done;		
 | 
						|
	}	
 | 
						|
 | 
						|
	/* pack the domain */
 | 
						|
 | 
						|
	if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
 | 
						|
		goto done;		
 | 
						|
	}
 | 
						|
	
 | 
						|
	/* Success */
 | 
						|
 | 
						|
	ret = true;
 | 
						|
 done:
 | 
						|
	TALLOC_FREE( dom_list );
 | 
						|
	
 | 
						|
	return ret;	
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
 | 
						|
{
 | 
						|
	struct winbindd_tdc_domain *dom_list = NULL;
 | 
						|
	size_t num_domains = 0;
 | 
						|
	int i;
 | 
						|
	struct winbindd_tdc_domain *d = NULL;	
 | 
						|
 | 
						|
	DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
 | 
						|
 | 
						|
	if ( !init_wcache() ) {
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	
 | 
						|
	/* fetch the list */
 | 
						|
 | 
						|
	wcache_tdc_fetch_list( &dom_list, &num_domains );
 | 
						|
	
 | 
						|
	for ( i=0; i<num_domains; i++ ) {
 | 
						|
		if ( strequal(name, dom_list[i].domain_name) ||
 | 
						|
		     strequal(name, dom_list[i].dns_name) )
 | 
						|
		{
 | 
						|
			DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
 | 
						|
				  name));
 | 
						|
			
 | 
						|
			d = TALLOC_P( ctx, struct winbindd_tdc_domain );
 | 
						|
			if ( !d )
 | 
						|
				break;			
 | 
						|
			
 | 
						|
			d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
 | 
						|
			d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
 | 
						|
			sid_copy( &d->sid, &dom_list[i].sid );
 | 
						|
			d->trust_flags   = dom_list[i].trust_flags;
 | 
						|
			d->trust_type    = dom_list[i].trust_type;
 | 
						|
			d->trust_attribs = dom_list[i].trust_attribs;
 | 
						|
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
        TALLOC_FREE( dom_list );
 | 
						|
	
 | 
						|
	return d;	
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
void wcache_tdc_clear( void )
 | 
						|
{
 | 
						|
	if ( !init_wcache() )
 | 
						|
		return;
 | 
						|
 | 
						|
	wcache_tdc_store_list( NULL, 0 );
 | 
						|
	
 | 
						|
	return;	
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************
 | 
						|
 ********************************************************************/
 | 
						|
 | 
						|
static void wcache_save_user_pwinfo(struct winbindd_domain *domain, 
 | 
						|
				    NTSTATUS status,
 | 
						|
				    const DOM_SID *user_sid,
 | 
						|
				    const char *homedir,
 | 
						|
				    const char *shell,
 | 
						|
				    const char *gecos,
 | 
						|
				    uint32 gid)
 | 
						|
{
 | 
						|
	struct cache_entry *centry;
 | 
						|
	fstring tmp;
 | 
						|
 | 
						|
	if ( (centry = centry_start(domain, status)) == NULL )
 | 
						|
		return;
 | 
						|
 | 
						|
	centry_put_string( centry, homedir );
 | 
						|
	centry_put_string( centry, shell );
 | 
						|
	centry_put_string( centry, gecos );
 | 
						|
	centry_put_uint32( centry, gid );
 | 
						|
	
 | 
						|
	centry_end(centry, "NSS/PWINFO/%s", sid_to_fstring(tmp, user_sid) );
 | 
						|
 | 
						|
	DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_dbg(user_sid) ));
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
}
 | 
						|
 | 
						|
NTSTATUS nss_get_info_cached( struct winbindd_domain *domain, 
 | 
						|
			      const DOM_SID *user_sid,
 | 
						|
			      TALLOC_CTX *ctx,
 | 
						|
			      ADS_STRUCT *ads, LDAPMessage *msg,
 | 
						|
			      char **homedir, char **shell, char **gecos,
 | 
						|
			      gid_t *p_gid)
 | 
						|
{
 | 
						|
	struct winbind_cache *cache = get_cache(domain);
 | 
						|
	struct cache_entry *centry = NULL;
 | 
						|
	NTSTATUS nt_status;
 | 
						|
	fstring tmp;
 | 
						|
 | 
						|
	if (!cache->tdb)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s",
 | 
						|
			      sid_to_fstring(tmp, user_sid));
 | 
						|
	
 | 
						|
	if (!centry)
 | 
						|
		goto do_query;
 | 
						|
 | 
						|
	*homedir = centry_string( centry, ctx );
 | 
						|
	*shell   = centry_string( centry, ctx );
 | 
						|
	*gecos   = centry_string( centry, ctx );
 | 
						|
	*p_gid   = centry_uint32( centry );	
 | 
						|
 | 
						|
	centry_free(centry);
 | 
						|
 | 
						|
	DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
 | 
						|
		  sid_string_dbg(user_sid)));
 | 
						|
 | 
						|
	return NT_STATUS_OK;
 | 
						|
 | 
						|
do_query:
 | 
						|
	
 | 
						|
	nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg, 
 | 
						|
				  homedir, shell, gecos, p_gid );
 | 
						|
 | 
						|
	if ( NT_STATUS_IS_OK(nt_status) ) {
 | 
						|
		wcache_save_user_pwinfo( domain, nt_status, user_sid,
 | 
						|
					 *homedir, *shell, *gecos, *p_gid );
 | 
						|
	}	
 | 
						|
 | 
						|
	if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
 | 
						|
		DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
 | 
						|
			 domain->name ));
 | 
						|
		set_domain_offline( domain );
 | 
						|
	}
 | 
						|
 | 
						|
	return nt_status;	
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* the cache 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,
 | 
						|
	rids_to_names,
 | 
						|
	query_user,
 | 
						|
	lookup_usergroups,
 | 
						|
	lookup_useraliases,
 | 
						|
	lookup_groupmem,
 | 
						|
	sequence_number,
 | 
						|
	lockout_policy,
 | 
						|
	password_policy,
 | 
						|
	trusted_domains
 | 
						|
};
 |