mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
148a102800
Previously LookupNames would fail if a name could not be translated, so winbindd clients like libwbclient couldn't differentiate between not being able to talk to a DC and just an unkown name. As a visible change this alters $ bin/wbinfo -n Idontexist failed to call wbcLookupName: WBC_ERR_DOMAIN_NOT_FOUND Could not lookup name Idontexist to $ bin/wbinfo -n Idontexist failed to call wbcLookupName: WBC_ERR_SOME_NOT_MAPPED Could not lookup name Idontexist Signed-off-by: Ralph Boehme <slow@samba.org> Reviewed-by: Stefan Metzmacher <metze@samba.org>
4925 lines
114 KiB
C
4925 lines
114 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 "system/filesys.h"
|
|
#include "winbindd.h"
|
|
#include "tdb_validate.h"
|
|
#include "../libcli/auth/libcli_auth.h"
|
|
#include "../librpc/gen_ndr/ndr_winbind.h"
|
|
#include "ads.h"
|
|
#include "nss_info.h"
|
|
#include "../libcli/security/security.h"
|
|
#include "passdb/machine_sid.h"
|
|
#include "util_tdb.h"
|
|
#include "libsmb/samlogon_cache.h"
|
|
#include "lib/namemap_cache.h"
|
|
#include "lib/util/string_wrappers.h"
|
|
|
|
#include "lib/crypto/gnutls_helpers.h"
|
|
#include <gnutls/crypto.h>
|
|
|
|
#undef DBGC_CLASS
|
|
#define DBGC_CLASS DBGC_WINBIND
|
|
|
|
#define WINBINDD_CACHE_VER1 1 /* initial db version */
|
|
#define WINBINDD_CACHE_VER2 2 /* second version with timeouts for NDR entries */
|
|
|
|
#define WINBINDD_CACHE_VERSION WINBINDD_CACHE_VER2
|
|
#define WINBINDD_CACHE_VERSION_KEYSTR "WINBINDD_CACHE_VERSION"
|
|
|
|
extern struct winbindd_methods reconnect_methods;
|
|
#ifdef HAVE_ADS
|
|
extern struct winbindd_methods reconnect_ads_methods;
|
|
#endif
|
|
extern struct winbindd_methods builtin_passdb_methods;
|
|
extern struct winbindd_methods sam_passdb_methods;
|
|
|
|
static void wcache_flush_cache(void);
|
|
|
|
static bool opt_nocache = False;
|
|
|
|
/*
|
|
* 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[] = {
|
|
"NDR/",
|
|
"SEQNUM/",
|
|
"TRUSTDOMCACHE/",
|
|
"WINBINDD_OFFLINE",
|
|
WINBINDD_CACHE_VERSION_KEYSTR,
|
|
NULL
|
|
};
|
|
|
|
bool winbindd_use_idmap_cache(void)
|
|
{
|
|
return !opt_nocache;
|
|
}
|
|
|
|
bool winbindd_use_cache(void)
|
|
{
|
|
return !opt_nocache;
|
|
}
|
|
|
|
void winbindd_set_use_cache(bool use_cache)
|
|
{
|
|
opt_nocache = !use_cache;
|
|
}
|
|
|
|
void winbindd_flush_caches(void)
|
|
{
|
|
/* We need to invalidate cached user list entries on a SIGHUP
|
|
otherwise cached access denied errors due to restrict anonymous
|
|
hang around until the sequence number changes. */
|
|
|
|
if (!wcache_invalidate_cache()) {
|
|
DBG_ERR("invalidating the cache failed; revalidate the cache\n");
|
|
if (!winbindd_cache_validate_and_initialize()) {
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
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;
|
|
}
|
|
|
|
struct winbind_cache {
|
|
TDB_CONTEXT *tdb;
|
|
};
|
|
|
|
struct cache_entry {
|
|
NTSTATUS status;
|
|
uint32_t sequence_number;
|
|
uint64_t timeout;
|
|
uint8_t *data;
|
|
uint32_t len, ofs;
|
|
};
|
|
|
|
void (*smb_panic_fn)(const char *const why) = smb_panic;
|
|
|
|
static struct winbind_cache *wcache;
|
|
|
|
static char *wcache_path(void)
|
|
{
|
|
/*
|
|
* Data needs to be kept persistent in state directory for
|
|
* running with "winbindd offline logon".
|
|
*/
|
|
return state_path(talloc_tos(), "winbindd_cache.tdb");
|
|
}
|
|
|
|
static void winbindd_domain_init_backend(struct winbindd_domain *domain)
|
|
{
|
|
if (domain->backend != NULL) {
|
|
return;
|
|
}
|
|
|
|
if (domain->internal) {
|
|
domain->backend = &builtin_passdb_methods;
|
|
}
|
|
|
|
if (dom_sid_equal(&domain->sid, &global_sid_Builtin)) {
|
|
domain->initialized = true;
|
|
}
|
|
|
|
if (strequal(domain->name, get_global_sam_name()) &&
|
|
sid_check_is_our_sam(&domain->sid))
|
|
{
|
|
domain->backend = &sam_passdb_methods;
|
|
}
|
|
|
|
if (!domain->initialized) {
|
|
/* We do not need a connection to an RW DC for cache operation */
|
|
init_dc_connection(domain, false);
|
|
}
|
|
|
|
#ifdef HAVE_ADS
|
|
if (domain->backend == NULL) {
|
|
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())
|
|
{
|
|
DBG_INFO("Setting ADS methods for domain %s\n",
|
|
domain->name);
|
|
domain->backend = &reconnect_ads_methods;
|
|
}
|
|
}
|
|
#endif /* HAVE_ADS */
|
|
|
|
if (domain->backend == NULL) {
|
|
DBG_INFO("Setting MS-RPC methods for domain %s\n", domain->name);
|
|
domain->backend = &reconnect_methods;
|
|
}
|
|
}
|
|
|
|
/* get the winbind_cache structure */
|
|
static struct winbind_cache *get_cache(struct winbindd_domain *domain)
|
|
{
|
|
struct winbind_cache *ret = wcache;
|
|
|
|
winbindd_domain_init_backend(domain);
|
|
|
|
if (ret != NULL) {
|
|
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) {
|
|
DBG_ERR("centry corruption? needed %u bytes, have %d\n",
|
|
(unsigned int)nbytes,
|
|
centry->len - centry->ofs);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
pull a uint64_t from a cache entry
|
|
*/
|
|
static uint64_t centry_uint64_t(struct cache_entry *centry)
|
|
{
|
|
uint64_t ret;
|
|
|
|
if (!centry_check_bytes(centry, 8)) {
|
|
smb_panic_fn("centry_uint64_t");
|
|
}
|
|
ret = BVAL(centry->data, centry->ofs);
|
|
centry->ofs += 8;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
pull a uint32_t from a cache entry
|
|
*/
|
|
static uint32_t centry_uint32(struct cache_entry *centry)
|
|
{
|
|
uint32_t 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_t from a cache entry
|
|
*/
|
|
static uint16_t centry_uint16(struct cache_entry *centry)
|
|
{
|
|
uint16_t ret;
|
|
if (!centry_check_bytes(centry, 2)) {
|
|
smb_panic_fn("centry_uint16");
|
|
}
|
|
ret = SVAL(centry->data, centry->ofs);
|
|
centry->ofs += 2;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
pull a uint8_t from a cache entry
|
|
*/
|
|
static uint8_t centry_uint8(struct cache_entry *centry)
|
|
{
|
|
uint8_t 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_t 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_t len;
|
|
char *ret;
|
|
|
|
len = centry_uint8(centry);
|
|
|
|
if (len != 16) {
|
|
DBG_ERR("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, struct dom_sid *sid)
|
|
{
|
|
char *sid_string;
|
|
bool ret;
|
|
|
|
sid_string = centry_string(centry, talloc_tos());
|
|
if (sid_string == NULL) {
|
|
return false;
|
|
}
|
|
ret = string_to_sid(sid, sid_string);
|
|
TALLOC_FREE(sid_string);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
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)
|
|
DBG_DEBUG("wcache_server_down: server for Domain %s down\n",
|
|
domain->name );
|
|
return ret;
|
|
}
|
|
|
|
struct wcache_seqnum_state {
|
|
uint32_t *seqnum;
|
|
uint32_t *last_seq_check;
|
|
};
|
|
|
|
static int wcache_seqnum_parser(TDB_DATA key, TDB_DATA data,
|
|
void *private_data)
|
|
{
|
|
struct wcache_seqnum_state *state = private_data;
|
|
|
|
if (data.dsize != 8) {
|
|
DBG_DEBUG("wcache_fetch_seqnum: invalid data size %d\n",
|
|
(int)data.dsize);
|
|
return -1;
|
|
}
|
|
|
|
*state->seqnum = IVAL(data.dptr, 0);
|
|
*state->last_seq_check = IVAL(data.dptr, 4);
|
|
return 0;
|
|
}
|
|
|
|
static bool wcache_fetch_seqnum(const char *domain_name, uint32_t *seqnum,
|
|
uint32_t *last_seq_check)
|
|
{
|
|
struct wcache_seqnum_state state = {
|
|
.seqnum = seqnum, .last_seq_check = last_seq_check
|
|
};
|
|
size_t len = strlen(domain_name);
|
|
char keystr[len+8];
|
|
TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
|
|
int ret;
|
|
|
|
if (wcache->tdb == NULL) {
|
|
DBG_DEBUG("wcache_fetch_seqnum: tdb == NULL\n");
|
|
return false;
|
|
}
|
|
|
|
snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
|
|
|
|
ret = tdb_parse_record(wcache->tdb, key, wcache_seqnum_parser,
|
|
&state);
|
|
return (ret == 0);
|
|
}
|
|
|
|
static NTSTATUS fetch_cache_seqnum( struct winbindd_domain *domain, time_t now )
|
|
{
|
|
uint32_t last_check, time_diff;
|
|
|
|
if (!wcache_fetch_seqnum(domain->name, &domain->sequence_number,
|
|
&last_check)) {
|
|
return NT_STATUS_UNSUCCESSFUL;
|
|
}
|
|
domain->last_seq_check = last_check;
|
|
|
|
/* have we expired? */
|
|
|
|
time_diff = now - domain->last_seq_check;
|
|
if ((int)time_diff > lp_winbind_cache_time()) {
|
|
DBG_DEBUG("fetch_cache_seqnum: timeout [%s][%u @ %u]\n",
|
|
domain->name, domain->sequence_number,
|
|
(uint32_t)domain->last_seq_check);
|
|
return NT_STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
DBG_DEBUG("fetch_cache_seqnum: success [%s][%u @ %u]\n",
|
|
domain->name, domain->sequence_number,
|
|
(uint32_t)domain->last_seq_check);
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
bool wcache_store_seqnum(const char *domain_name, uint32_t seqnum,
|
|
time_t last_seq_check)
|
|
{
|
|
size_t len = strlen(domain_name);
|
|
char keystr[len+8];
|
|
TDB_DATA key = { .dptr = (uint8_t *)keystr, .dsize = sizeof(keystr) };
|
|
uint8_t buf[8];
|
|
int ret;
|
|
|
|
if (wcache->tdb == NULL) {
|
|
DBG_DEBUG("wcache_store_seqnum: wcache->tdb == NULL\n");
|
|
return false;
|
|
}
|
|
|
|
snprintf(keystr, sizeof(keystr), "SEQNUM/%s", domain_name);
|
|
|
|
SIVAL(buf, 0, seqnum);
|
|
SIVAL(buf, 4, last_seq_check);
|
|
|
|
ret = tdb_store(wcache->tdb, key, make_tdb_data(buf, sizeof(buf)),
|
|
TDB_REPLACE);
|
|
if (ret != 0) {
|
|
DBG_DEBUG("tdb_store_bystring failed: %s\n",
|
|
tdb_errorstr(wcache->tdb));
|
|
return false;
|
|
}
|
|
|
|
DBG_DEBUG("wcache_store_seqnum: success [%s][%u @ %u]\n",
|
|
domain_name, seqnum, (unsigned)last_seq_check);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool store_cache_seqnum( struct winbindd_domain *domain )
|
|
{
|
|
return wcache_store_seqnum(domain->name, domain->sequence_number,
|
|
domain->last_seq_check);
|
|
}
|
|
|
|
/*
|
|
refresh the domain sequence number on timeout.
|
|
*/
|
|
|
|
static void refresh_sequence_number(struct winbindd_domain *domain)
|
|
{
|
|
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 );
|
|
|
|
time_diff = t - domain->last_seq_check;
|
|
|
|
/* see if we have to refetch the domain sequence number */
|
|
if ((time_diff < cache_time) &&
|
|
(domain->sequence_number != DOM_SEQUENCE_NONE) &&
|
|
NT_STATUS_IS_OK(domain->last_status)) {
|
|
DBG_DEBUG("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) &&
|
|
(domain->sequence_number != DOM_SEQUENCE_NONE) &&
|
|
NT_STATUS_IS_OK(domain->last_status)) {
|
|
goto done;
|
|
}
|
|
|
|
/* just use the current time */
|
|
domain->last_status = NT_STATUS_OK;
|
|
domain->sequence_number = time(NULL);
|
|
domain->last_seq_check = time(NULL);
|
|
|
|
/* save the new sequence number in the cache */
|
|
store_cache_seqnum( domain );
|
|
|
|
done:
|
|
DBG_DEBUG("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() && get_global_winbindd_state_offline()) {
|
|
DBG_DEBUG("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) {
|
|
DBG_DEBUG("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)) {
|
|
DBG_DEBUG("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 or it did not timeout then it is OK */
|
|
if (wcache_server_down(domain)
|
|
|| ((centry->sequence_number == domain->sequence_number)
|
|
&& ((time_t)centry->timeout > time(NULL)))) {
|
|
DBG_DEBUG("centry_expired: Key %s for domain %s is good.\n",
|
|
keystr, domain->name );
|
|
return false;
|
|
}
|
|
|
|
DBG_DEBUG("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 < 16) {
|
|
/* huh? corrupt cache? */
|
|
DBG_DEBUG("wcache_fetch_raw: Corrupt cache for key %s "
|
|
"(len < 16)?\n", kstr);
|
|
centry_free(centry);
|
|
return NULL;
|
|
}
|
|
|
|
centry->status = centry_ntstatus(centry);
|
|
centry->sequence_number = centry_uint32(centry);
|
|
centry->timeout = centry_uint64_t(centry);
|
|
|
|
return centry;
|
|
}
|
|
|
|
static bool is_my_own_sam_domain(struct winbindd_domain *domain)
|
|
{
|
|
if (strequal(domain->name, get_global_sam_name()) &&
|
|
sid_check_is_our_sam(&domain->sid)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool is_builtin_domain(struct winbindd_domain *domain)
|
|
{
|
|
if (strequal(domain->name, "BUILTIN") &&
|
|
sid_check_is_builtin(&domain->sid)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
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;
|
|
int ret;
|
|
|
|
if (!winbindd_use_cache() ||
|
|
is_my_own_sam_domain(domain) ||
|
|
is_builtin_domain(domain)) {
|
|
return NULL;
|
|
}
|
|
|
|
refresh_sequence_number(domain);
|
|
|
|
va_start(ap, format);
|
|
ret = vasprintf(&kstr, format, ap);
|
|
va_end(ap);
|
|
|
|
if (ret == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
centry = wcache_fetch_raw(kstr);
|
|
if (centry == NULL) {
|
|
free(kstr);
|
|
return NULL;
|
|
}
|
|
|
|
if (centry_expired(domain, kstr, centry)) {
|
|
|
|
DBG_DEBUG("wcache_fetch: entry %s expired for domain %s\n",
|
|
kstr, domain->name );
|
|
|
|
centry_free(centry);
|
|
free(kstr);
|
|
return NULL;
|
|
}
|
|
|
|
DBG_DEBUG("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;
|
|
int ret;
|
|
|
|
va_start(ap, format);
|
|
ret = vasprintf(&kstr, format, ap);
|
|
va_end(ap);
|
|
|
|
if (ret == -1) {
|
|
return;
|
|
}
|
|
|
|
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_t 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) {
|
|
DBG_ERR("out of memory: needed %d bytes in centry_expand\n", centry->len);
|
|
smb_panic_fn("out of memory in centry_expand");
|
|
}
|
|
}
|
|
|
|
/*
|
|
push a uint64_t into a centry
|
|
*/
|
|
static void centry_put_uint64_t(struct cache_entry *centry, uint64_t v)
|
|
{
|
|
centry_expand(centry, 8);
|
|
SBVAL(centry->data, centry->ofs, v);
|
|
centry->ofs += 8;
|
|
}
|
|
|
|
/*
|
|
push a uint32_t into a centry
|
|
*/
|
|
static void centry_put_uint32(struct cache_entry *centry, uint32_t v)
|
|
{
|
|
centry_expand(centry, 4);
|
|
SIVAL(centry->data, centry->ofs, v);
|
|
centry->ofs += 4;
|
|
}
|
|
|
|
/*
|
|
push a uint16_t into a centry
|
|
*/
|
|
static void centry_put_uint16(struct cache_entry *centry, uint16_t v)
|
|
{
|
|
centry_expand(centry, 2);
|
|
SSVAL(centry->data, centry->ofs, v);
|
|
centry->ofs += 2;
|
|
}
|
|
|
|
/*
|
|
push a uint8_t into a centry
|
|
*/
|
|
static void centry_put_uint8(struct cache_entry *centry, uint8_t 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)
|
|
{
|
|
size_t 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) {
|
|
DBG_DEBUG("centry_put_string: truncating len (%zu) 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_t 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 struct dom_sid *sid)
|
|
{
|
|
struct dom_sid_buf sid_string;
|
|
centry_put_string(centry, dom_sid_str_buf(sid, &sid_string));
|
|
}
|
|
|
|
|
|
/*
|
|
put NTSTATUS into a centry
|
|
*/
|
|
static void centry_put_ntstatus(struct cache_entry *centry, NTSTATUS status)
|
|
{
|
|
uint32_t 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()
|
|
*/
|
|
static 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_t, centry->len);
|
|
centry->ofs = 0;
|
|
centry->sequence_number = domain->sequence_number;
|
|
centry->timeout = lp_winbind_cache_time() + time(NULL);
|
|
centry_put_ntstatus(centry, status);
|
|
centry_put_uint32(centry, centry->sequence_number);
|
|
centry_put_uint64_t(centry, centry->timeout);
|
|
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;
|
|
int ret;
|
|
|
|
if (!winbindd_use_cache()) {
|
|
return;
|
|
}
|
|
|
|
va_start(ap, format);
|
|
ret = vasprintf(&kstr, format, ap);
|
|
va_end(ap);
|
|
|
|
if (ret == -1) {
|
|
return;
|
|
}
|
|
|
|
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 struct dom_sid *sid,
|
|
enum lsa_SidType type)
|
|
{
|
|
bool ok;
|
|
|
|
ok = namemap_cache_set_name2sid(domain_name, name, sid, type,
|
|
time(NULL) + lp_winbind_cache_time());
|
|
if (!ok) {
|
|
DBG_DEBUG("namemap_cache_set_name2sid failed\n");
|
|
}
|
|
|
|
/*
|
|
* Don't store the reverse mapping. The name came from user
|
|
* input, and we might not have the correct capitalization,
|
|
* which is important for nsswitch.
|
|
*/
|
|
}
|
|
|
|
static void wcache_save_sid_to_name(struct winbindd_domain *domain, NTSTATUS status,
|
|
const struct dom_sid *sid, const char *domain_name, const char *name, enum lsa_SidType type)
|
|
{
|
|
bool ok;
|
|
|
|
ok = namemap_cache_set_sid2name(sid, domain_name, name, type,
|
|
time(NULL) + lp_winbind_cache_time());
|
|
if (!ok) {
|
|
DBG_DEBUG("namemap_cache_set_sid2name failed\n");
|
|
}
|
|
|
|
if (type != SID_NAME_UNKNOWN) {
|
|
ok = namemap_cache_set_name2sid(
|
|
domain_name, name, sid, type,
|
|
time(NULL) + lp_winbind_cache_time());
|
|
if (!ok) {
|
|
DBG_DEBUG("namemap_cache_set_name2sid failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
DBG_DEBUG("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);
|
|
|
|
DBG_DEBUG("wcache_save_password_policy: %s\n", domain->name);
|
|
|
|
centry_free(centry);
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
|
|
static void wcache_save_username_alias(struct winbindd_domain *domain,
|
|
NTSTATUS status,
|
|
const char *name, const char *alias)
|
|
{
|
|
struct cache_entry *centry;
|
|
fstring uname;
|
|
|
|
if ( (centry = centry_start(domain, status)) == NULL )
|
|
return;
|
|
|
|
centry_put_string( centry, alias );
|
|
|
|
fstrcpy(uname, name);
|
|
(void)strupper_m(uname);
|
|
centry_end(centry, "NSS/NA/%s", uname);
|
|
|
|
DBG_DEBUG("wcache_save_username_alias: %s -> %s\n", name, alias );
|
|
|
|
centry_free(centry);
|
|
}
|
|
|
|
static void wcache_save_alias_username(struct winbindd_domain *domain,
|
|
NTSTATUS status,
|
|
const char *alias, const char *name)
|
|
{
|
|
struct cache_entry *centry;
|
|
fstring uname;
|
|
|
|
if ( (centry = centry_start(domain, status)) == NULL )
|
|
return;
|
|
|
|
centry_put_string( centry, name );
|
|
|
|
fstrcpy(uname, alias);
|
|
(void)strupper_m(uname);
|
|
centry_end(centry, "NSS/AN/%s", uname);
|
|
|
|
DBG_DEBUG("wcache_save_alias_username: %s -> %s\n", alias, name );
|
|
|
|
centry_free(centry);
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
|
|
NTSTATUS resolve_username_to_alias( TALLOC_CTX *mem_ctx,
|
|
struct winbindd_domain *domain,
|
|
const char *name, char **alias )
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
char *upper_name;
|
|
|
|
if ( domain->internal )
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
upper_name = talloc_strdup_upper(mem_ctx, name);
|
|
if (upper_name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
centry = wcache_fetch(cache, domain, "NSS/NA/%s", upper_name);
|
|
|
|
talloc_free(upper_name);
|
|
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
status = centry->status;
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
centry_free(centry);
|
|
return status;
|
|
}
|
|
|
|
*alias = centry_string( centry, mem_ctx );
|
|
|
|
centry_free(centry);
|
|
|
|
DBG_DEBUG("resolve_username_to_alias: [Cached] - mapped %s to %s\n",
|
|
name, *alias ? *alias : "(none)");
|
|
|
|
return (*alias) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
do_query:
|
|
|
|
/* If its not in cache and we are offline, then fail */
|
|
|
|
if (is_domain_offline(domain)) {
|
|
DBG_DEBUG("resolve_username_to_alias: rejecting query "
|
|
"in offline mode\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
status = nss_map_to_alias( mem_ctx, domain->name, name, alias );
|
|
|
|
if ( NT_STATUS_IS_OK( status ) ) {
|
|
wcache_save_username_alias(domain, status, name, *alias);
|
|
}
|
|
|
|
if ( NT_STATUS_EQUAL( status, NT_STATUS_NONE_MAPPED ) ) {
|
|
wcache_save_username_alias(domain, status, name, "(NULL)");
|
|
}
|
|
|
|
DBG_INFO("resolve_username_to_alias: backend query returned %s\n",
|
|
nt_errstr(status));
|
|
|
|
if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
|
|
set_domain_offline( domain );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/***************************************************************************
|
|
***************************************************************************/
|
|
|
|
NTSTATUS resolve_alias_to_username( TALLOC_CTX *mem_ctx,
|
|
struct winbindd_domain *domain,
|
|
const char *alias, char **name )
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
char *upper_name;
|
|
|
|
if ( domain->internal )
|
|
return NT_STATUS_NOT_SUPPORTED;
|
|
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
upper_name = talloc_strdup(mem_ctx, alias);
|
|
if (upper_name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
if (!strupper_m(upper_name)) {
|
|
talloc_free(upper_name);
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
centry = wcache_fetch(cache, domain, "NSS/AN/%s", upper_name);
|
|
|
|
talloc_free(upper_name);
|
|
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
status = centry->status;
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
centry_free(centry);
|
|
return status;
|
|
}
|
|
|
|
*name = centry_string( centry, mem_ctx );
|
|
|
|
centry_free(centry);
|
|
|
|
DBG_DEBUG("resolve_alias_to_username: [Cached] - mapped %s to %s\n",
|
|
alias, *name ? *name : "(none)");
|
|
|
|
return (*name) ? NT_STATUS_OK : NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
do_query:
|
|
|
|
/* If its not in cache and we are offline, then fail */
|
|
|
|
if (is_domain_offline(domain)) {
|
|
DBG_DEBUG("resolve_alias_to_username: rejecting query "
|
|
"in offline mode\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
/* an alias cannot contain a domain prefix or '@' */
|
|
|
|
if (strchr(alias, '\\') || strchr(alias, '@')) {
|
|
DBG_DEBUG("resolve_alias_to_username: skipping fully "
|
|
"qualified name %s\n", alias);
|
|
return NT_STATUS_OBJECT_NAME_INVALID;
|
|
}
|
|
|
|
status = nss_map_from_alias( mem_ctx, domain->name, alias, name );
|
|
|
|
if ( NT_STATUS_IS_OK( status ) ) {
|
|
wcache_save_alias_username( domain, status, alias, *name );
|
|
}
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
|
|
wcache_save_alias_username(domain, status, alias, "(NULL)");
|
|
}
|
|
|
|
DBG_INFO("resolve_alias_to_username: backend query returned %s\n",
|
|
nt_errstr(status));
|
|
|
|
if ( NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND) ) {
|
|
set_domain_offline( domain );
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS wcache_cached_creds_exist(struct winbindd_domain *domain, const struct dom_sid *sid)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
int ret;
|
|
struct dom_sid_buf tmp;
|
|
fstring key_str;
|
|
uint32_t 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", dom_sid_str_buf(sid, &tmp));
|
|
|
|
ret = tdb_exists(cache->tdb, string_tdb_data(key_str));
|
|
if (ret != 1) {
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
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 struct dom_sid *sid,
|
|
const uint8_t **cached_nt_pass,
|
|
const uint8_t **cached_salt)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
uint32_t rid;
|
|
struct dom_sid_buf sidstr;
|
|
|
|
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",
|
|
dom_sid_str_buf(sid, &sidstr));
|
|
if (!centry) {
|
|
DBG_DEBUG("wcache_get_creds: entry for [CRED/%s] not found\n",
|
|
dom_sid_str_buf(sid, &sidstr));
|
|
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
|
}
|
|
|
|
/*
|
|
* We don't use the time element at this moment,
|
|
* but we have to consume it, so that we don't
|
|
* need to change the disk format of the cache.
|
|
*/
|
|
(void)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_t *)centry_hash16(centry, mem_ctx);
|
|
if (*cached_nt_pass == NULL) {
|
|
|
|
dom_sid_str_buf(sid, &sidstr);
|
|
|
|
/* Bad (old) cred cache. Delete and pretend we
|
|
don't have it. */
|
|
DBG_WARNING("wcache_get_creds: bad entry for [CRED/%s] - deleting\n",
|
|
sidstr.buf);
|
|
wcache_delete("CRED/%s", sidstr.buf);
|
|
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_t *)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;
|
|
|
|
DBG_DEBUG("wcache_get_creds: [Cached] - cached creds for user %s status: %s\n",
|
|
dom_sid_str_buf(sid, &sidstr),
|
|
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,
|
|
const struct dom_sid *sid,
|
|
const uint8_t nt_pass[NT_HASH_LEN])
|
|
{
|
|
struct cache_entry *centry;
|
|
struct dom_sid_buf sid_str;
|
|
uint32_t rid;
|
|
uint8_t cred_salt[NT_HASH_LEN];
|
|
uint8_t salted_hash[NT_HASH_LEN];
|
|
gnutls_hash_hd_t hash_hnd = NULL;
|
|
int rc;
|
|
|
|
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);
|
|
|
|
rc = gnutls_hash_init(&hash_hnd, GNUTLS_DIG_MD5);
|
|
if (rc < 0) {
|
|
centry_free(centry);
|
|
return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
|
|
}
|
|
|
|
rc = gnutls_hash(hash_hnd, cred_salt, 16);
|
|
if (rc < 0) {
|
|
gnutls_hash_deinit(hash_hnd, NULL);
|
|
centry_free(centry);
|
|
return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
|
|
}
|
|
rc = gnutls_hash(hash_hnd, nt_pass, 16);
|
|
if (rc < 0) {
|
|
gnutls_hash_deinit(hash_hnd, NULL);
|
|
centry_free(centry);
|
|
return gnutls_error_to_ntstatus(rc, NT_STATUS_HASH_NOT_SUPPORTED);
|
|
}
|
|
gnutls_hash_deinit(hash_hnd, salted_hash);
|
|
|
|
centry_put_hash16(centry, salted_hash);
|
|
centry_put_hash16(centry, cred_salt);
|
|
centry_end(centry, "CRED/%s", dom_sid_str_buf(sid, &sid_str));
|
|
|
|
DBG_DEBUG("wcache_save_creds: %s\n", sid_str.buf);
|
|
|
|
centry_free(centry);
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
|
|
/* Query display info. This is the basic user list fn */
|
|
NTSTATUS wb_cache_query_user_list(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint32_t **prids)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
uint32_t num_rids = 0;
|
|
uint32_t *rids = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i, retry;
|
|
bool old_status = domain->online;
|
|
|
|
*prids = NULL;
|
|
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
do_fetch_cache:
|
|
num_rids = centry_uint32(centry);
|
|
|
|
if (num_rids == 0) {
|
|
goto do_cached;
|
|
}
|
|
|
|
rids = talloc_array(mem_ctx, uint32_t, num_rids);
|
|
if (rids == NULL) {
|
|
centry_free(centry);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i=0; i<num_rids; i++) {
|
|
rids[i] = centry_uint32(centry);
|
|
}
|
|
|
|
do_cached:
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("query_user_list: [Cached] - cached list for domain %s status: %s\n",
|
|
domain->name, nt_errstr(status) );
|
|
|
|
centry_free(centry);
|
|
return status;
|
|
|
|
do_query:
|
|
|
|
/* 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 {
|
|
|
|
DBG_DEBUG("query_user_list: [Cached] - doing backend query for list for domain %s\n",
|
|
domain->name );
|
|
|
|
rids = NULL;
|
|
status = domain->backend->query_user_list(domain, mem_ctx,
|
|
&rids);
|
|
num_rids = talloc_array_length(rids);
|
|
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
DBG_NOTICE("query_user_list: returned 0x%08x, "
|
|
"retrying\n", NT_STATUS_V(status));
|
|
}
|
|
reset_cm_connection_on_error(domain, NULL, status);
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_UNSUCCESSFUL)) {
|
|
DBG_NOTICE("query_user_list: flushing "
|
|
"connection cache\n");
|
|
invalidate_cm_connection(domain);
|
|
}
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
/* store partial response. */
|
|
if (num_rids > 0) {
|
|
/*
|
|
* humm, what about the status used for cache?
|
|
* Should it be NT_STATUS_OK?
|
|
*/
|
|
break;
|
|
}
|
|
/*
|
|
* domain is offline now, and there is no user entries,
|
|
* try to fetch from cache again.
|
|
*/
|
|
if (cache->tdb && !domain->online && !domain->internal && old_status) {
|
|
centry = wcache_fetch(cache, domain, "UL/%s", domain->name);
|
|
/* partial response... */
|
|
if (!centry) {
|
|
goto skip_save;
|
|
} else {
|
|
goto do_fetch_cache;
|
|
}
|
|
} else {
|
|
goto skip_save;
|
|
}
|
|
}
|
|
|
|
} while (NT_STATUS_V(status) == NT_STATUS_V(NT_STATUS_UNSUCCESSFUL) &&
|
|
(retry++ < 5));
|
|
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
centry = centry_start(domain, status);
|
|
if (!centry)
|
|
goto skip_save;
|
|
centry_put_uint32(centry, num_rids);
|
|
for (i=0; i<num_rids; i++) {
|
|
centry_put_uint32(centry, rids[i]);
|
|
}
|
|
centry_end(centry, "UL/%s", domain->name);
|
|
centry_free(centry);
|
|
|
|
*prids = rids;
|
|
|
|
skip_save:
|
|
return status;
|
|
}
|
|
|
|
/* list all domain groups */
|
|
NTSTATUS wb_cache_enum_dom_groups(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint32_t *num_entries,
|
|
struct wb_acct_info **info)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
do_fetch_cache:
|
|
*num_entries = centry_uint32(centry);
|
|
|
|
if (*num_entries == 0)
|
|
goto do_cached;
|
|
|
|
(*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
|
|
if (! (*info)) {
|
|
smb_panic_fn("enum_dom_groups out of memory");
|
|
}
|
|
for (i=0; i<(*num_entries); i++) {
|
|
(*info)[i].acct_name = centry_string(centry, (*info));
|
|
(*info)[i].acct_desc = centry_string(centry, (*info));
|
|
(*info)[i].rid = centry_uint32(centry);
|
|
}
|
|
|
|
do_cached:
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("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;
|
|
|
|
DBG_DEBUG("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);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (cache->tdb &&
|
|
!domain->online &&
|
|
!domain->internal &&
|
|
old_status) {
|
|
centry = wcache_fetch(cache, domain, "GL/%s/domain", domain->name);
|
|
if (centry) {
|
|
goto do_fetch_cache;
|
|
}
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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 */
|
|
NTSTATUS wb_cache_enum_local_groups(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint32_t *num_entries,
|
|
struct wb_acct_info **info)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
do_fetch_cache:
|
|
*num_entries = centry_uint32(centry);
|
|
|
|
if (*num_entries == 0)
|
|
goto do_cached;
|
|
|
|
(*info) = talloc_array(mem_ctx, struct wb_acct_info, *num_entries);
|
|
if (! (*info)) {
|
|
smb_panic_fn("enum_dom_groups out of memory");
|
|
}
|
|
for (i=0; i<(*num_entries); i++) {
|
|
(*info)[i].acct_name = centry_string(centry, (*info));
|
|
(*info)[i].acct_desc = centry_string(centry, (*info));
|
|
(*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)) {
|
|
DBG_DEBUG("enum_local_groups: returning cached user list and server was down\n");
|
|
status = NT_STATUS_MORE_PROCESSING_REQUIRED;
|
|
} else
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("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;
|
|
|
|
DBG_DEBUG("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);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (cache->tdb &&
|
|
!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
centry = wcache_fetch(cache, domain, "GL/%s/local", domain->name);
|
|
if (centry) {
|
|
goto do_fetch_cache;
|
|
}
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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;
|
|
}
|
|
|
|
struct wcache_name_to_sid_state {
|
|
struct dom_sid *sid;
|
|
enum lsa_SidType *type;
|
|
bool offline;
|
|
bool found;
|
|
};
|
|
|
|
static void wcache_name_to_sid_fn(const struct dom_sid *sid,
|
|
enum lsa_SidType type,
|
|
bool expired,
|
|
void *private_data)
|
|
{
|
|
struct wcache_name_to_sid_state *state = private_data;
|
|
|
|
*state->sid = *sid;
|
|
*state->type = type;
|
|
state->found = (!expired || state->offline);
|
|
}
|
|
|
|
static NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
|
|
const char *domain_name,
|
|
const char *name,
|
|
struct dom_sid *sid,
|
|
enum lsa_SidType *type)
|
|
{
|
|
struct wcache_name_to_sid_state state = {
|
|
.sid = sid, .type = type, .found = false,
|
|
.offline = is_domain_offline(domain),
|
|
};
|
|
bool ok;
|
|
|
|
ok = namemap_cache_find_name(domain_name, name, wcache_name_to_sid_fn,
|
|
&state);
|
|
if (!ok) {
|
|
DBG_DEBUG("namemap_cache_find_name failed\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
if (!state.found) {
|
|
DBG_DEBUG("cache entry not found\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/* convert a single name to a sid in a domain */
|
|
NTSTATUS wb_cache_name_to_sid(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const char *domain_name,
|
|
const char *name,
|
|
uint32_t flags,
|
|
struct dom_sid *sid,
|
|
enum lsa_SidType *type)
|
|
{
|
|
NTSTATUS status;
|
|
bool was_online;
|
|
const char *dom_name;
|
|
|
|
was_online = domain->online;
|
|
|
|
ZERO_STRUCTP(sid);
|
|
*type = SID_NAME_UNKNOWN;
|
|
|
|
status = wcache_name_to_sid(domain, domain_name, name, sid, type);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
DBG_DEBUG("name_to_sid: [Cached] - doing backend query for name for domain %s\n",
|
|
domain->name );
|
|
|
|
winbindd_domain_init_backend(domain);
|
|
status = domain->backend->name_to_sid(domain, mem_ctx, domain_name,
|
|
name, flags, &dom_name, sid, type);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND))
|
|
{
|
|
if (!domain->internal && was_online) {
|
|
/* Set the domain offline and query the cache again */
|
|
set_domain_offline(domain);
|
|
return wcache_name_to_sid(domain,
|
|
domain_name,
|
|
name,
|
|
sid,
|
|
type);
|
|
}
|
|
}
|
|
/* and save it */
|
|
|
|
if (domain->online && NT_STATUS_IS_OK(status)) {
|
|
wcache_save_name_to_sid(domain, status, domain_name, name, sid,
|
|
*type);
|
|
|
|
/* Only save the reverse mapping if this was not a UPN */
|
|
if (!strchr(name, '@')) {
|
|
if (!strupper_m(discard_const_p(char, domain_name))) {
|
|
return NT_STATUS_INVALID_PARAMETER;
|
|
}
|
|
(void)strlower_m(discard_const_p(char, name));
|
|
wcache_save_sid_to_name(domain, status, sid,
|
|
dom_name, name, *type);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
struct wcache_sid_to_name_state {
|
|
TALLOC_CTX *mem_ctx;
|
|
char **domain_name;
|
|
char **name;
|
|
enum lsa_SidType *type;
|
|
bool offline;
|
|
bool found;
|
|
};
|
|
|
|
static void wcache_sid_to_name_fn(const char *domain,
|
|
const char *name,
|
|
enum lsa_SidType type,
|
|
bool expired,
|
|
void *private_data)
|
|
{
|
|
struct wcache_sid_to_name_state *state = private_data;
|
|
|
|
*state->domain_name = talloc_strdup(state->mem_ctx, domain);
|
|
if (*state->domain_name == NULL) {
|
|
return;
|
|
}
|
|
*state->name = talloc_strdup(state->mem_ctx, name);
|
|
if (*state->name == NULL) {
|
|
return;
|
|
}
|
|
*state->type = type;
|
|
state->found = (!expired || state->offline);
|
|
}
|
|
|
|
static NTSTATUS wcache_sid_to_name(struct winbindd_domain *domain,
|
|
const struct dom_sid *sid,
|
|
TALLOC_CTX *mem_ctx,
|
|
char **domain_name,
|
|
char **name,
|
|
enum lsa_SidType *type)
|
|
{
|
|
struct wcache_sid_to_name_state state = {
|
|
.mem_ctx = mem_ctx, .found = false,
|
|
.domain_name = domain_name, .name = name, .type = type,
|
|
.offline = is_domain_offline(domain)
|
|
};
|
|
bool ok;
|
|
|
|
ok = namemap_cache_find_sid(sid, wcache_sid_to_name_fn, &state);
|
|
if (!ok) {
|
|
DBG_DEBUG("namemap_cache_find_name failed\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
if (!state.found) {
|
|
DBG_DEBUG("cache entry not found\n");
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
if (*type == SID_NAME_UNKNOWN) {
|
|
return NT_STATUS_NONE_MAPPED;
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
/* convert a sid to a user or group name. The sid is guaranteed to be in the domain
|
|
given */
|
|
NTSTATUS wb_cache_sid_to_name(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *sid,
|
|
char **domain_name,
|
|
char **name,
|
|
enum lsa_SidType *type)
|
|
{
|
|
NTSTATUS status;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
|
|
type);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
*name = NULL;
|
|
*domain_name = NULL;
|
|
|
|
DBG_DEBUG("sid_to_name: [Cached] - doing backend query for name for domain %s\n",
|
|
domain->name );
|
|
|
|
winbindd_domain_init_backend(domain);
|
|
|
|
status = domain->backend->sid_to_name(domain, mem_ctx, sid, domain_name, name, type);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
NTSTATUS cache_status;
|
|
cache_status = wcache_sid_to_name(domain, sid, mem_ctx,
|
|
domain_name, name, type);
|
|
return cache_status;
|
|
}
|
|
}
|
|
/* and save it */
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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;
|
|
}
|
|
|
|
NTSTATUS wb_cache_rids_to_names(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *domain_sid,
|
|
uint32_t *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;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
*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++) {
|
|
struct dom_sid sid;
|
|
NTSTATUS status;
|
|
enum lsa_SidType type;
|
|
char *dom, *name;
|
|
|
|
if (!sid_compose(&sid, domain_sid, rids[i])) {
|
|
result = NT_STATUS_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
status = wcache_sid_to_name(domain, &sid, *names, &dom,
|
|
&name, &type);
|
|
|
|
(*types)[i] = SID_NAME_UNKNOWN;
|
|
(*names)[i] = talloc_strdup(*names, "");
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
/* not cached */
|
|
goto do_query;
|
|
}
|
|
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
have_mapped = true;
|
|
(*types)[i] = type;
|
|
|
|
if (*domain_name == NULL) {
|
|
*domain_name = dom;
|
|
} else {
|
|
TALLOC_FREE(dom);
|
|
}
|
|
|
|
(*names)[i] = name;
|
|
|
|
} else if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) {
|
|
have_unmapped = true;
|
|
} else {
|
|
/* something's definitely wrong */
|
|
result = status;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (NT_STATUS_EQUAL(result, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(result, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (cache->tdb &&
|
|
!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
have_mapped = have_unmapped = false;
|
|
|
|
*names = talloc_array(mem_ctx, char *, num_rids);
|
|
if (*names == NULL) {
|
|
result = NT_STATUS_NO_MEMORY;
|
|
goto error;
|
|
}
|
|
|
|
*types = talloc_array(mem_ctx, enum lsa_SidType,
|
|
num_rids);
|
|
if (*types == NULL) {
|
|
result = NT_STATUS_NO_MEMORY;
|
|
goto error;
|
|
}
|
|
|
|
for (i=0; i<num_rids; i++) {
|
|
struct dom_sid sid;
|
|
NTSTATUS status;
|
|
enum lsa_SidType type;
|
|
char *dom, *name;
|
|
|
|
if (!sid_compose(&sid, domain_sid, rids[i])) {
|
|
result = NT_STATUS_INTERNAL_ERROR;
|
|
goto error;
|
|
}
|
|
|
|
status = wcache_sid_to_name(domain, &sid,
|
|
*names, &dom,
|
|
&name, &type);
|
|
|
|
(*types)[i] = SID_NAME_UNKNOWN;
|
|
(*names)[i] = talloc_strdup(*names, "");
|
|
|
|
if (NT_STATUS_IS_OK(status)) {
|
|
have_mapped = true;
|
|
(*types)[i] = type;
|
|
|
|
if (*domain_name == NULL) {
|
|
*domain_name = dom;
|
|
} else {
|
|
TALLOC_FREE(dom);
|
|
}
|
|
|
|
(*names)[i] = name;
|
|
|
|
} else if (NT_STATUS_EQUAL(
|
|
status,
|
|
NT_STATUS_NONE_MAPPED)) {
|
|
have_unmapped = true;
|
|
} else {
|
|
/* something's definitely wrong */
|
|
result = status;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
if (!have_mapped) {
|
|
return NT_STATUS_NONE_MAPPED;
|
|
}
|
|
if (!have_unmapped) {
|
|
return NT_STATUS_OK;
|
|
}
|
|
return STATUS_SOME_UNMAPPED;
|
|
}
|
|
}
|
|
/*
|
|
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++) {
|
|
struct 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);
|
|
|
|
for (i=0; i<num_rids; i++) {
|
|
struct 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;
|
|
}
|
|
|
|
static NTSTATUS wcache_query_user(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *user_sid,
|
|
struct wbint_userinfo *info)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
struct dom_sid_buf sid_string;
|
|
|
|
if (cache->tdb == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
centry = wcache_fetch(
|
|
cache, domain, "U/%s", dom_sid_str_buf(user_sid, &sid_string));
|
|
if (centry == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
/* 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->domain_name = centry_string(centry, mem_ctx);
|
|
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->uid = centry_uint32(centry);
|
|
info->primary_gid = centry_uint32(centry);
|
|
info->primary_group_name = centry_string(centry, mem_ctx);
|
|
centry_sid(centry, &info->user_sid);
|
|
centry_sid(centry, &info->group_sid);
|
|
}
|
|
|
|
DBG_DEBUG("query_user: [Cached] - cached info for domain %s status: "
|
|
"%s\n", domain->name, nt_errstr(status) );
|
|
|
|
centry_free(centry);
|
|
return status;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Query a fullname from the username cache (for further gecos processing)
|
|
*
|
|
* @param domain A pointer to the winbindd_domain struct.
|
|
* @param mem_ctx The talloc context.
|
|
* @param user_sid The user sid.
|
|
* @param full_name A pointer to the full_name string.
|
|
*
|
|
* @return NTSTATUS code
|
|
*/
|
|
NTSTATUS wcache_query_user_fullname(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *user_sid,
|
|
const char **full_name)
|
|
{
|
|
NTSTATUS status;
|
|
struct wbint_userinfo info;
|
|
|
|
status = wcache_query_user(domain, mem_ctx, user_sid, &info);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (info.full_name != NULL) {
|
|
*full_name = talloc_strdup(mem_ctx, info.full_name);
|
|
if (*full_name == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
static NTSTATUS wcache_lookup_usergroups(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *user_sid,
|
|
uint32_t *pnum_sids,
|
|
struct dom_sid **psids)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
uint32_t i, num_sids;
|
|
struct dom_sid *sids;
|
|
struct dom_sid_buf sid_string;
|
|
|
|
if (cache->tdb == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
centry = wcache_fetch(
|
|
cache,
|
|
domain,
|
|
"UG/%s",
|
|
dom_sid_str_buf(user_sid, &sid_string));
|
|
if (centry == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
num_sids = centry_uint32(centry);
|
|
sids = talloc_array(mem_ctx, struct dom_sid, num_sids);
|
|
if (sids == NULL) {
|
|
centry_free(centry);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i=0; i<num_sids; i++) {
|
|
centry_sid(centry, &sids[i]);
|
|
}
|
|
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("lookup_usergroups: [Cached] - cached info for domain %s "
|
|
"status: %s\n", domain->name, nt_errstr(status));
|
|
|
|
centry_free(centry);
|
|
|
|
*pnum_sids = num_sids;
|
|
*psids = sids;
|
|
return status;
|
|
}
|
|
|
|
/* Lookup groups a user is a member of. */
|
|
NTSTATUS wb_cache_lookup_usergroups(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *user_sid,
|
|
uint32_t *num_groups,
|
|
struct dom_sid **user_gids)
|
|
{
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
struct dom_sid_buf sid_string;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
|
|
num_groups, user_gids);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
(*num_groups) = 0;
|
|
(*user_gids) = NULL;
|
|
|
|
DBG_DEBUG("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_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
NTSTATUS cache_status;
|
|
cache_status = wcache_lookup_usergroups(domain, mem_ctx, user_sid,
|
|
num_groups, user_gids);
|
|
return cache_status;
|
|
}
|
|
}
|
|
if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
|
|
goto skip_save;
|
|
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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", dom_sid_str_buf(user_sid, &sid_string));
|
|
centry_free(centry);
|
|
|
|
skip_save:
|
|
return status;
|
|
}
|
|
|
|
static char *wcache_make_sidlist(TALLOC_CTX *mem_ctx, uint32_t num_sids,
|
|
const struct dom_sid *sids)
|
|
{
|
|
uint32_t i;
|
|
char *sidlist;
|
|
|
|
sidlist = talloc_strdup(mem_ctx, "");
|
|
if (sidlist == NULL) {
|
|
return NULL;
|
|
}
|
|
for (i=0; i<num_sids; i++) {
|
|
struct dom_sid_buf tmp;
|
|
sidlist = talloc_asprintf_append_buffer(
|
|
sidlist,
|
|
"/%s",
|
|
dom_sid_str_buf(&sids[i], &tmp));
|
|
if (sidlist == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return sidlist;
|
|
}
|
|
|
|
static NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint32_t num_sids,
|
|
const struct dom_sid *sids,
|
|
uint32_t *pnum_aliases,
|
|
uint32_t **paliases)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
uint32_t i, num_aliases;
|
|
uint32_t *aliases;
|
|
NTSTATUS status;
|
|
char *sidlist;
|
|
|
|
if (cache->tdb == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
if (num_sids == 0) {
|
|
*pnum_aliases = 0;
|
|
*paliases = 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. */
|
|
|
|
sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
|
|
if (sidlist == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
centry = wcache_fetch(cache, domain, "UA%s", sidlist);
|
|
TALLOC_FREE(sidlist);
|
|
if (centry == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
num_aliases = centry_uint32(centry);
|
|
aliases = talloc_array(mem_ctx, uint32_t, num_aliases);
|
|
if (aliases == NULL) {
|
|
centry_free(centry);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i=0; i<num_aliases; i++) {
|
|
aliases[i] = centry_uint32(centry);
|
|
}
|
|
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("lookup_useraliases: [Cached] - cached info for domain: %s "
|
|
"status %s\n", domain->name, nt_errstr(status));
|
|
|
|
centry_free(centry);
|
|
|
|
*pnum_aliases = num_aliases;
|
|
*paliases = aliases;
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS wb_cache_lookup_useraliases(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
uint32_t num_sids,
|
|
const struct dom_sid *sids,
|
|
uint32_t *num_aliases,
|
|
uint32_t **alias_rids)
|
|
{
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
char *sidlist;
|
|
uint32_t i;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
status = wcache_lookup_useraliases(domain, mem_ctx, num_sids, sids,
|
|
num_aliases, alias_rids);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
(*num_aliases) = 0;
|
|
(*alias_rids) = NULL;
|
|
|
|
DBG_DEBUG("lookup_usergroups: [Cached] - doing backend query for info "
|
|
"for domain %s\n", domain->name );
|
|
|
|
sidlist = wcache_make_sidlist(talloc_tos(), num_sids, sids);
|
|
if (sidlist == NULL) {
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
status = domain->backend->lookup_useraliases(domain, mem_ctx,
|
|
num_sids, sids,
|
|
num_aliases, alias_rids);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
NTSTATUS cache_status;
|
|
cache_status = wcache_lookup_useraliases(domain, mem_ctx, num_sids,
|
|
sids, num_aliases, alias_rids);
|
|
return cache_status;
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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 wcache_lookup_aliasmem(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *group_sid,
|
|
uint32_t *num_names,
|
|
struct dom_sid **sid_mem)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
struct dom_sid_buf sid_string;
|
|
|
|
if (cache->tdb == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
centry = wcache_fetch(cache,
|
|
domain,
|
|
"AM/%s",
|
|
dom_sid_str_buf(group_sid, &sid_string));
|
|
if (centry == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
*sid_mem = NULL;
|
|
|
|
*num_names = centry_uint32(centry);
|
|
if (*num_names == 0) {
|
|
centry_free(centry);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
*sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
|
|
if (*sid_mem == NULL) {
|
|
TALLOC_FREE(*sid_mem);
|
|
centry_free(centry);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i = 0; i < (*num_names); i++) {
|
|
centry_sid(centry, &(*sid_mem)[i]);
|
|
}
|
|
|
|
status = centry->status;
|
|
|
|
D_DEBUG("[Cached] - cached info for domain %s "
|
|
"status: %s\n",
|
|
domain->name,
|
|
nt_errstr(status));
|
|
|
|
centry_free(centry);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS wb_cache_lookup_aliasmem(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *group_sid,
|
|
enum lsa_SidType type,
|
|
uint32_t *num_sids,
|
|
struct dom_sid **sid_mem)
|
|
{
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
struct dom_sid_buf sid_string;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
status = wcache_lookup_aliasmem(domain,
|
|
mem_ctx,
|
|
group_sid,
|
|
num_sids,
|
|
sid_mem);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
(*num_sids) = 0;
|
|
(*sid_mem) = NULL;
|
|
|
|
D_DEBUG("[Cached] - doing backend query for info for domain %s\n",
|
|
domain->name);
|
|
|
|
status = domain->backend->lookup_aliasmem(domain,
|
|
mem_ctx,
|
|
group_sid,
|
|
type,
|
|
num_sids,
|
|
sid_mem);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal && !domain->online && old_status) {
|
|
NTSTATUS cache_status;
|
|
cache_status = wcache_lookup_aliasmem(domain,
|
|
mem_ctx,
|
|
group_sid,
|
|
num_sids,
|
|
sid_mem);
|
|
return cache_status;
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
centry = centry_start(domain, status);
|
|
if (!centry)
|
|
goto skip_save;
|
|
centry_put_uint32(centry, *num_sids);
|
|
for (i = 0; i < (*num_sids); i++) {
|
|
centry_put_sid(centry, &(*sid_mem)[i]);
|
|
}
|
|
centry_end(centry, "AM/%s", dom_sid_str_buf(group_sid, &sid_string));
|
|
centry_free(centry);
|
|
|
|
skip_save:
|
|
return status;
|
|
}
|
|
|
|
static NTSTATUS wcache_lookup_groupmem(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *group_sid,
|
|
uint32_t *num_names,
|
|
struct dom_sid **sid_mem, char ***names,
|
|
uint32_t **name_types)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
struct dom_sid_buf sid_string;
|
|
|
|
if (cache->tdb == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
centry = wcache_fetch(
|
|
cache,
|
|
domain,
|
|
"GM/%s",
|
|
dom_sid_str_buf(group_sid, &sid_string));
|
|
if (centry == NULL) {
|
|
return NT_STATUS_NOT_FOUND;
|
|
}
|
|
|
|
*sid_mem = NULL;
|
|
*names = NULL;
|
|
*name_types = NULL;
|
|
|
|
*num_names = centry_uint32(centry);
|
|
if (*num_names == 0) {
|
|
centry_free(centry);
|
|
return NT_STATUS_OK;
|
|
}
|
|
|
|
*sid_mem = talloc_array(mem_ctx, struct dom_sid, *num_names);
|
|
*names = talloc_array(mem_ctx, char *, *num_names);
|
|
*name_types = talloc_array(mem_ctx, uint32_t, *num_names);
|
|
|
|
if ((*sid_mem == NULL) || (*names == NULL) || (*name_types == NULL)) {
|
|
TALLOC_FREE(*sid_mem);
|
|
TALLOC_FREE(*names);
|
|
TALLOC_FREE(*name_types);
|
|
centry_free(centry);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i=0; i<(*num_names); i++) {
|
|
centry_sid(centry, &(*sid_mem)[i]);
|
|
(*names)[i] = centry_string(centry, mem_ctx);
|
|
(*name_types)[i] = centry_uint32(centry);
|
|
}
|
|
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("lookup_groupmem: [Cached] - cached info for domain %s "
|
|
"status: %s\n", domain->name, nt_errstr(status));
|
|
|
|
centry_free(centry);
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS wb_cache_lookup_groupmem(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
const struct dom_sid *group_sid,
|
|
enum lsa_SidType type,
|
|
uint32_t *num_names,
|
|
struct dom_sid **sid_mem,
|
|
char ***names,
|
|
uint32_t **name_types)
|
|
{
|
|
struct cache_entry *centry = NULL;
|
|
NTSTATUS status;
|
|
unsigned int i;
|
|
struct dom_sid_buf sid_string;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
status = wcache_lookup_groupmem(domain, mem_ctx, group_sid, num_names,
|
|
sid_mem, names, name_types);
|
|
if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
|
return status;
|
|
}
|
|
|
|
(*num_names) = 0;
|
|
(*sid_mem) = NULL;
|
|
(*names) = NULL;
|
|
(*name_types) = NULL;
|
|
|
|
DBG_DEBUG("lookup_groupmem: [Cached] - doing backend query for info for domain %s\n",
|
|
domain->name );
|
|
|
|
status = domain->backend->lookup_groupmem(domain, mem_ctx, group_sid,
|
|
type, num_names,
|
|
sid_mem, names, name_types);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
NTSTATUS cache_status;
|
|
cache_status = wcache_lookup_groupmem(domain, mem_ctx, group_sid,
|
|
num_names, sid_mem, names,
|
|
name_types);
|
|
return cache_status;
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
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",
|
|
dom_sid_str_buf(group_sid, &sid_string));
|
|
centry_free(centry);
|
|
|
|
skip_save:
|
|
return status;
|
|
}
|
|
|
|
/* find the sequence number for a domain */
|
|
NTSTATUS wb_cache_sequence_number(struct winbindd_domain *domain,
|
|
uint32_t *seq)
|
|
{
|
|
refresh_sequence_number(domain);
|
|
|
|
*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 */
|
|
NTSTATUS wb_cache_trusted_domains(struct winbindd_domain *domain,
|
|
TALLOC_CTX *mem_ctx,
|
|
struct netr_DomainTrustList *trusts)
|
|
{
|
|
NTSTATUS status;
|
|
struct winbind_cache *cache;
|
|
struct winbindd_tdc_domain *dom_list = NULL;
|
|
size_t num_domains = 0;
|
|
bool retval = false;
|
|
size_t i;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
trusts->count = 0;
|
|
trusts->array = NULL;
|
|
|
|
cache = get_cache(domain);
|
|
if (!cache || !cache->tdb) {
|
|
goto do_query;
|
|
}
|
|
|
|
if (domain->online) {
|
|
goto do_query;
|
|
}
|
|
|
|
retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
|
|
if (!retval || !num_domains || !dom_list) {
|
|
TALLOC_FREE(dom_list);
|
|
goto do_query;
|
|
}
|
|
|
|
do_fetch_cache:
|
|
trusts->array = talloc_zero_array(mem_ctx, struct netr_DomainTrust, num_domains);
|
|
if (!trusts->array) {
|
|
TALLOC_FREE(dom_list);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
for (i = 0; i < num_domains; i++) {
|
|
struct netr_DomainTrust *trust;
|
|
struct dom_sid *sid;
|
|
struct winbindd_domain *dom;
|
|
|
|
dom = find_domain_from_name_noinit(dom_list[i].domain_name);
|
|
if (dom && dom->internal) {
|
|
continue;
|
|
}
|
|
|
|
trust = &trusts->array[trusts->count];
|
|
trust->netbios_name = talloc_strdup(trusts->array, dom_list[i].domain_name);
|
|
trust->dns_name = talloc_strdup(trusts->array, dom_list[i].dns_name);
|
|
sid = talloc(trusts->array, struct dom_sid);
|
|
if (!trust->netbios_name || !trust->dns_name ||
|
|
!sid) {
|
|
TALLOC_FREE(dom_list);
|
|
TALLOC_FREE(trusts->array);
|
|
return NT_STATUS_NO_MEMORY;
|
|
}
|
|
|
|
trust->trust_flags = dom_list[i].trust_flags;
|
|
trust->trust_attributes = dom_list[i].trust_attribs;
|
|
trust->trust_type = dom_list[i].trust_type;
|
|
sid_copy(sid, &dom_list[i].sid);
|
|
trust->sid = sid;
|
|
trusts->count++;
|
|
}
|
|
|
|
TALLOC_FREE(dom_list);
|
|
return NT_STATUS_OK;
|
|
|
|
do_query:
|
|
DBG_DEBUG("trusted_domains: [Cached] - doing backend query for info for domain %s\n",
|
|
domain->name );
|
|
|
|
status = domain->backend->trusted_domains(domain, mem_ctx, trusts);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
retval = wcache_tdc_fetch_list(&dom_list, &num_domains);
|
|
if (retval && num_domains && dom_list) {
|
|
TALLOC_FREE(trusts->array);
|
|
trusts->count = 0;
|
|
goto do_fetch_cache;
|
|
}
|
|
}
|
|
}
|
|
/* 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;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* get lockout policy */
|
|
NTSTATUS wb_cache_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;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
|
|
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
do_fetch_cache:
|
|
policy->lockout_duration = centry_nttime(centry);
|
|
policy->lockout_window = centry_nttime(centry);
|
|
policy->lockout_threshold = centry_uint16(centry);
|
|
|
|
status = centry->status;
|
|
|
|
DBG_DEBUG("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);
|
|
|
|
DBG_DEBUG("lockout_policy: [Cached] - doing backend query for info for domain %s\n",
|
|
domain->name );
|
|
|
|
status = domain->backend->lockout_policy(domain, mem_ctx, policy);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (cache->tdb &&
|
|
!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
centry = wcache_fetch(cache, domain, "LOC_POL/%s", domain->name);
|
|
if (centry) {
|
|
goto do_fetch_cache;
|
|
}
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return status;
|
|
}
|
|
wcache_save_lockout_policy(domain, status, policy);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* get password policy */
|
|
NTSTATUS wb_cache_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;
|
|
bool old_status;
|
|
|
|
old_status = domain->online;
|
|
if (!cache->tdb)
|
|
goto do_query;
|
|
|
|
centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
|
|
|
|
if (!centry)
|
|
goto do_query;
|
|
|
|
do_fetch_cache:
|
|
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;
|
|
|
|
DBG_DEBUG("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);
|
|
|
|
DBG_DEBUG("password_policy: [Cached] - doing backend query for info for domain %s\n",
|
|
domain->name );
|
|
|
|
status = domain->backend->password_policy(domain, mem_ctx, policy);
|
|
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
|
|
NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
|
|
if (!domain->internal && old_status) {
|
|
set_domain_offline(domain);
|
|
}
|
|
if (cache->tdb &&
|
|
!domain->internal &&
|
|
!domain->online &&
|
|
old_status) {
|
|
centry = wcache_fetch(cache, domain, "PWD_POL/%s", domain->name);
|
|
if (centry) {
|
|
goto do_fetch_cache;
|
|
}
|
|
}
|
|
}
|
|
/* and save it */
|
|
refresh_sequence_number(domain);
|
|
if (!NT_STATUS_IS_OK(status)) {
|
|
return 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,
|
|
const struct dom_sid *sid)
|
|
{
|
|
fstring key_str;
|
|
struct dom_sid_buf sid_string;
|
|
struct winbind_cache *cache;
|
|
|
|
/* don't 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);
|
|
|
|
if (!cache->tdb) {
|
|
return;
|
|
}
|
|
|
|
/* Clear U/SID cache entry */
|
|
fstr_sprintf(key_str, "U/%s", dom_sid_str_buf(sid, &sid_string));
|
|
DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
|
|
tdb_delete(cache->tdb, string_tdb_data(key_str));
|
|
|
|
/* Clear UG/SID cache entry */
|
|
fstr_sprintf(key_str, "UG/%s", dom_sid_str_buf(sid, &sid_string));
|
|
DBG_DEBUG("wcache_invalidate_samlogon: clearing %s\n", key_str);
|
|
tdb_delete(cache->tdb, string_tdb_data(key_str));
|
|
|
|
/* Samba/winbindd never needs this. */
|
|
netsamlogon_clear_cached_user(sid);
|
|
}
|
|
|
|
bool wcache_invalidate_cache(void)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
|
|
for (domain = domain_list(); domain; domain = domain->next) {
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
|
|
DBG_DEBUG("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 wcache_invalidate_cache_noinit(void)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
|
|
for (domain = domain_list(); domain; domain = domain->next) {
|
|
struct winbind_cache *cache;
|
|
|
|
/* Skip uninitialized domains. */
|
|
if (!domain->initialized && !domain->internal) {
|
|
continue;
|
|
}
|
|
|
|
cache = get_cache(domain);
|
|
|
|
DBG_DEBUG("wcache_invalidate_cache: invalidating cache "
|
|
"entries for %s\n", domain->name);
|
|
if (cache) {
|
|
if (cache->tdb) {
|
|
tdb_traverse(cache->tdb, traverse_fn, NULL);
|
|
/*
|
|
* Flushing cache has nothing to with domains.
|
|
* return here if we successfully flushed once.
|
|
* To avoid unnecessary traversing the cache.
|
|
*/
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool init_wcache(void)
|
|
{
|
|
char *db_path;
|
|
|
|
if (wcache == NULL) {
|
|
wcache = SMB_XMALLOC_P(struct winbind_cache);
|
|
ZERO_STRUCTP(wcache);
|
|
}
|
|
|
|
if (wcache->tdb != NULL)
|
|
return true;
|
|
|
|
db_path = wcache_path();
|
|
if (db_path == NULL) {
|
|
return false;
|
|
}
|
|
|
|
/* when working offline we must not clear the cache on restart */
|
|
wcache->tdb = tdb_open_log(db_path,
|
|
WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
|
|
TDB_INCOMPATIBLE_HASH |
|
|
(lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
|
|
O_RDWR|O_CREAT, 0600);
|
|
TALLOC_FREE(db_path);
|
|
if (wcache->tdb == NULL) {
|
|
DBG_ERR("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 = false;
|
|
uint32_t vers = 0;
|
|
bool ok;
|
|
|
|
if (!init_wcache()) {
|
|
DBG_ERR("initialize_winbindd_cache: init_wcache failed.\n");
|
|
return false;
|
|
}
|
|
|
|
/* Check version number. */
|
|
ok = tdb_fetch_uint32(wcache->tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers);
|
|
if (!ok) {
|
|
DBG_DEBUG("Failed to get cache version\n");
|
|
cache_bad = true;
|
|
}
|
|
if (vers != WINBINDD_CACHE_VERSION) {
|
|
DBG_DEBUG("Invalid cache version %u != %u\n",
|
|
vers,
|
|
WINBINDD_CACHE_VERSION);
|
|
cache_bad = true;
|
|
}
|
|
|
|
if (cache_bad) {
|
|
char *db_path;
|
|
|
|
DBG_NOTICE("initialize_winbindd_cache: clearing cache "
|
|
"and re-creating with version number %d\n",
|
|
WINBINDD_CACHE_VERSION);
|
|
|
|
tdb_close(wcache->tdb);
|
|
wcache->tdb = NULL;
|
|
|
|
db_path = wcache_path();
|
|
if (db_path == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (unlink(db_path) == -1) {
|
|
DBG_ERR("initialize_winbindd_cache: unlink %s failed %s\n",
|
|
db_path,
|
|
strerror(errno) );
|
|
TALLOC_FREE(db_path);
|
|
return false;
|
|
}
|
|
TALLOC_FREE(db_path);
|
|
if (!init_wcache()) {
|
|
DBG_ERR("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)) {
|
|
DBG_ERR("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;
|
|
}
|
|
}
|
|
|
|
bool lookup_cached_sid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
|
|
char **domain_name, char **name,
|
|
enum lsa_SidType *type)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
NTSTATUS status;
|
|
|
|
domain = find_lookup_domain_from_sid(sid);
|
|
if (domain == NULL) {
|
|
return false;
|
|
}
|
|
status = wcache_sid_to_name(domain, sid, mem_ctx, domain_name, name,
|
|
type);
|
|
return NT_STATUS_IS_OK(status);
|
|
}
|
|
|
|
bool lookup_cached_name(const char *namespace,
|
|
const char *domain_name,
|
|
const char *name,
|
|
struct dom_sid *sid,
|
|
enum lsa_SidType *type)
|
|
{
|
|
struct winbindd_domain *domain;
|
|
NTSTATUS status;
|
|
bool original_online_state;
|
|
|
|
domain = find_lookup_domain_from_name(namespace);
|
|
if (domain == NULL) {
|
|
return false;
|
|
}
|
|
|
|
/* 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;
|
|
status = wcache_name_to_sid(domain, domain_name, name, sid, type);
|
|
domain->online = original_online_state;
|
|
|
|
return NT_STATUS_IS_OK(status);
|
|
}
|
|
|
|
/*
|
|
* Cache a name to sid without checking the sequence number.
|
|
* Used when caching from a trusted PAC.
|
|
*/
|
|
|
|
void cache_name2sid_trusted(struct winbindd_domain *domain,
|
|
const char *domain_name,
|
|
const char *name,
|
|
enum lsa_SidType type,
|
|
const struct dom_sid *sid)
|
|
{
|
|
/*
|
|
* Ensure we store the mapping with the
|
|
* existing sequence number from the cache.
|
|
*/
|
|
get_cache(domain);
|
|
(void)fetch_cache_seqnum(domain, time(NULL));
|
|
wcache_save_name_to_sid(domain,
|
|
NT_STATUS_OK,
|
|
domain_name,
|
|
name,
|
|
sid,
|
|
type);
|
|
}
|
|
|
|
void cache_name2sid(struct winbindd_domain *domain,
|
|
const char *domain_name, const char *name,
|
|
enum lsa_SidType type, const struct dom_sid *sid)
|
|
{
|
|
refresh_sequence_number(domain);
|
|
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)) {
|
|
DBG_DEBUG("deleting centry %s\n", (const char *)kbuf.dptr);
|
|
tdb_delete(the_tdb, kbuf);
|
|
}
|
|
|
|
centry_free(centry);
|
|
return 0;
|
|
}
|
|
|
|
/* flush the cache */
|
|
static void wcache_flush_cache(void)
|
|
{
|
|
char *db_path;
|
|
|
|
if (!wcache)
|
|
return;
|
|
if (wcache->tdb) {
|
|
tdb_close(wcache->tdb);
|
|
wcache->tdb = NULL;
|
|
}
|
|
if (!winbindd_use_cache()) {
|
|
return;
|
|
}
|
|
|
|
db_path = wcache_path();
|
|
if (db_path == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* when working offline we must not clear the cache on restart */
|
|
wcache->tdb = tdb_open_log(db_path,
|
|
WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
|
|
TDB_INCOMPATIBLE_HASH |
|
|
(lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST)),
|
|
O_RDWR|O_CREAT, 0600);
|
|
TALLOC_FREE(db_path);
|
|
if (!wcache->tdb) {
|
|
DBG_ERR("Failed to open winbindd_cache.tdb!\n");
|
|
return;
|
|
}
|
|
|
|
tdb_traverse(wcache->tdb, traverse_fn_cleanup, NULL);
|
|
|
|
DBG_DEBUG("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) {
|
|
DBG_ERR("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 struct dom_sid *sid)
|
|
{
|
|
struct winbind_cache *cache = get_cache(domain);
|
|
NTSTATUS status;
|
|
int ret;
|
|
struct cred_list *cred, *next, *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;
|
|
struct dom_sid_buf tmp;
|
|
|
|
DBG_DEBUG("we already have an entry, deleting that\n");
|
|
|
|
fstr_sprintf(key_str, "CRED/%s", dom_sid_str_buf(sid, &tmp));
|
|
|
|
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 < 0) || (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) {
|
|
DBG_DEBUG("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:
|
|
for (cred = wcache_cred_list; cred; cred = next) {
|
|
next = cred->next;
|
|
DLIST_REMOVE(wcache_cred_list, cred);
|
|
SAFE_FREE(cred);
|
|
}
|
|
SAFE_FREE(oldest);
|
|
|
|
return status;
|
|
}
|
|
|
|
/* Change the global online/offline state. */
|
|
bool set_global_winbindd_state_offline(void)
|
|
{
|
|
bool ok;
|
|
uint8_t buf[4] = {0};
|
|
TDB_DATA data = {
|
|
.dptr = buf,
|
|
.dsize = sizeof(buf)
|
|
};
|
|
int rc;
|
|
|
|
DBG_NOTICE("Offline requested\n");
|
|
|
|
if (wcache == NULL || wcache->tdb == NULL) {
|
|
DBG_NOTICE("Winbind cache doesn't exist yet\n");
|
|
return false;
|
|
}
|
|
|
|
if (!lp_winbind_offline_logon()) {
|
|
DBG_DEBUG("Rejecting request to set winbind offline, "
|
|
"offline logons are disabled in smb.conf\n");
|
|
return false;
|
|
}
|
|
|
|
ok = get_global_winbindd_state_offline();
|
|
if (ok) {
|
|
return true;
|
|
}
|
|
|
|
PUSH_LE_U32(buf, 0, time(NULL));
|
|
|
|
rc = tdb_store_bystring(wcache->tdb,
|
|
"WINBINDD_OFFLINE",
|
|
data,
|
|
TDB_INSERT);
|
|
if (rc != 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
void set_global_winbindd_state_online(void)
|
|
{
|
|
DBG_DEBUG("set_global_winbindd_state_online: online requested.\n");
|
|
|
|
if (!lp_winbind_offline_logon()) {
|
|
DBG_DEBUG("Rejecting request to set winbind online, "
|
|
"offline logons are disabled in smb.conf.\n");
|
|
return;
|
|
}
|
|
|
|
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)
|
|
{
|
|
TDB_DATA data;
|
|
|
|
data = tdb_fetch_bystring(wcache->tdb, "WINBINDD_OFFLINE");
|
|
if (data.dptr == NULL || data.dsize != 4) {
|
|
DBG_DEBUG("Offline state not set.\n");
|
|
SAFE_FREE(data.dptr);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/***********************************************************************
|
|
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 *)smb_memdup(data.dptr, data.dsize);
|
|
if (!centry->data) {
|
|
SAFE_FREE(centry);
|
|
return NULL;
|
|
}
|
|
centry->len = data.dsize;
|
|
centry->ofs = 0;
|
|
|
|
if (centry->len < 16) {
|
|
/* huh? corrupt cache? */
|
|
DBG_ERR("create_centry_validate: Corrupt cache for key %s "
|
|
"(len < 16) ?\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);
|
|
centry->timeout = centry_uint64_t(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) {
|
|
DBG_ERR("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_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);
|
|
struct 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_string(centry, mem_ctx);
|
|
(void)centry_uint32(centry);
|
|
(void)centry_uint32(centry);
|
|
(void)centry_string(centry, mem_ctx);
|
|
(void)centry_sid(centry, &sid);
|
|
(void)centry_sid(centry, &sid);
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("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;
|
|
}
|
|
DBG_DEBUG("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;
|
|
}
|
|
DBG_DEBUG("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;
|
|
}
|
|
DBG_DEBUG("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_t num_entries, i;
|
|
|
|
if (!centry) {
|
|
return 1;
|
|
}
|
|
|
|
num_entries = (int32_t)centry_uint32(centry);
|
|
|
|
for (i=0; i< num_entries; i++) {
|
|
(void)centry_uint32(centry);
|
|
}
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("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_t 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;
|
|
}
|
|
DBG_DEBUG("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_t num_groups, i;
|
|
|
|
if (!centry) {
|
|
return 1;
|
|
}
|
|
|
|
num_groups = centry_uint32(centry);
|
|
|
|
for (i=0; i< num_groups; i++) {
|
|
struct dom_sid sid;
|
|
centry_sid(centry, &sid);
|
|
}
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("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_t 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;
|
|
}
|
|
DBG_DEBUG("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_t num_names, i;
|
|
|
|
if (!centry) {
|
|
return 1;
|
|
}
|
|
|
|
num_names = centry_uint32(centry);
|
|
|
|
for (i=0; i< num_names; i++) {
|
|
struct dom_sid sid;
|
|
centry_sid(centry, &sid);
|
|
(void)centry_string(centry, mem_ctx);
|
|
(void)centry_uint32(centry);
|
|
}
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("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) {
|
|
DBG_ERR("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
|
|
keystr);
|
|
state->bad_entry = true;
|
|
state->success = false;
|
|
return 1;
|
|
}
|
|
|
|
DBG_DEBUG("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) {
|
|
DBG_ERR("validate_de: Corrupt cache for key %s (len == 0) ?\n",
|
|
keystr);
|
|
state->bad_entry = true;
|
|
state->success = false;
|
|
return 1;
|
|
}
|
|
|
|
DBG_DEBUG("validate_de: %s ok\n", keystr);
|
|
return 0;
|
|
}
|
|
|
|
static int validate_nss_an(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 );
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("validate_pwinfo: %s ok\n", keystr);
|
|
return 0;
|
|
}
|
|
|
|
static int validate_nss_na(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 );
|
|
|
|
centry_free(centry);
|
|
|
|
if (!(state->success)) {
|
|
return 1;
|
|
}
|
|
DBG_DEBUG("%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) {
|
|
DBG_ERR("validate_trustdomcache: Corrupt cache for "
|
|
"key %s (len ==0) ?\n", keystr);
|
|
state->bad_entry = true;
|
|
state->success = false;
|
|
return 1;
|
|
}
|
|
|
|
DBG_DEBUG("validate_trustdomcache: %s ok\n"
|
|
" Don't trust me, I am a DUMMY!\n",
|
|
keystr);
|
|
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) {
|
|
DBG_ERR("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;
|
|
}
|
|
DBG_DEBUG("validate_offline: %s ok\n", keystr);
|
|
return 0;
|
|
}
|
|
|
|
static int validate_ndr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
|
|
struct tdb_validation_status *state)
|
|
{
|
|
/*
|
|
* Ignore validation for now. The proper way to do this is with a
|
|
* checksum. Just pure parsing does not really catch much.
|
|
*/
|
|
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) {
|
|
DBG_ERR("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;
|
|
}
|
|
|
|
DBG_DEBUG("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},
|
|
{"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},
|
|
{"TRUSTDOMCACHE/", validate_trustdomcache},
|
|
{"NSS/NA/", validate_nss_na},
|
|
{"NSS/AN/", validate_nss_an},
|
|
{"WINBINDD_OFFLINE", validate_offline},
|
|
{"NDR/", validate_ndr},
|
|
{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 ||
|
|
strncmp("NDR/", (const char *)kbuf.dptr, 4) == 0) {
|
|
max_key_len = 1024 * 1024;
|
|
}
|
|
if (kbuf.dsize > max_key_len) {
|
|
DBG_ERR("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;
|
|
}
|
|
}
|
|
|
|
DBG_ERR("cache_traverse_validate_fn: unknown cache entry\nkey :\n");
|
|
dump_data(0, (uint8_t *)kbuf.dptr, kbuf.dsize);
|
|
DBG_ERR("data :\n");
|
|
dump_data(0, (uint8_t *)dbuf.dptr, dbuf.dsize);
|
|
v_state->unknown_key = true;
|
|
v_state->success = false;
|
|
return 1; /* terminate. */
|
|
}
|
|
|
|
static void validate_panic(const char *const why)
|
|
{
|
|
DBG_ERR("validating cache: would panic %s\n"
|
|
"exiting instead (cache validation mode)\n", why );
|
|
exit(47);
|
|
}
|
|
|
|
static int wbcache_update_centry_fn(TDB_CONTEXT *tdb,
|
|
TDB_DATA key,
|
|
TDB_DATA data,
|
|
void *state)
|
|
{
|
|
uint64_t ctimeout;
|
|
TDB_DATA blob;
|
|
|
|
if (is_non_centry_key(key)) {
|
|
return 0;
|
|
}
|
|
|
|
if (data.dptr == NULL || data.dsize == 0) {
|
|
if (tdb_delete(tdb, key) < 0) {
|
|
DBG_ERR("tdb_delete for [%s] failed!\n",
|
|
key.dptr);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/* add timeout to blob (uint64_t) */
|
|
blob.dsize = data.dsize + 8;
|
|
|
|
blob.dptr = SMB_XMALLOC_ARRAY(uint8_t, blob.dsize);
|
|
if (blob.dptr == NULL) {
|
|
return 1;
|
|
}
|
|
memset(blob.dptr, 0, blob.dsize);
|
|
|
|
/* copy status and seqnum */
|
|
memcpy(blob.dptr, data.dptr, 8);
|
|
|
|
/* add timeout */
|
|
ctimeout = lp_winbind_cache_time() + time(NULL);
|
|
SBVAL(blob.dptr, 8, ctimeout);
|
|
|
|
/* copy the rest */
|
|
memcpy(blob.dptr + 16, data.dptr + 8, data.dsize - 8);
|
|
|
|
if (tdb_store(tdb, key, blob, TDB_REPLACE) < 0) {
|
|
DBG_ERR("tdb_store to update [%s] failed!\n",
|
|
key.dptr);
|
|
SAFE_FREE(blob.dptr);
|
|
return 1;
|
|
}
|
|
|
|
SAFE_FREE(blob.dptr);
|
|
return 0;
|
|
}
|
|
|
|
static bool wbcache_upgrade_v1_to_v2(TDB_CONTEXT *tdb)
|
|
{
|
|
int rc;
|
|
|
|
DBG_NOTICE("Upgrade to version 2 of the winbindd_cache.tdb\n");
|
|
|
|
rc = tdb_traverse(tdb, wbcache_update_centry_fn, NULL);
|
|
if (rc < 0) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/***********************************************************************
|
|
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;
|
|
char *tdb_path = NULL;
|
|
TDB_CONTEXT *tdb = NULL;
|
|
uint32_t vers_id;
|
|
bool ok;
|
|
|
|
DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
|
|
smb_panic_fn = validate_panic;
|
|
|
|
tdb_path = wcache_path();
|
|
if (tdb_path == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
tdb = tdb_open_log(tdb_path,
|
|
WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
|
|
TDB_INCOMPATIBLE_HASH |
|
|
( lp_winbind_offline_logon()
|
|
? TDB_DEFAULT
|
|
: TDB_DEFAULT | TDB_CLEAR_IF_FIRST ),
|
|
O_RDWR|O_CREAT,
|
|
0600);
|
|
if (!tdb) {
|
|
DBG_ERR("winbindd_validate_cache: "
|
|
"error opening/initializing tdb\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Version check and upgrade code. */
|
|
if (!tdb_fetch_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, &vers_id)) {
|
|
DBG_DEBUG("Fresh database\n");
|
|
tdb_store_uint32(tdb, WINBINDD_CACHE_VERSION_KEYSTR, WINBINDD_CACHE_VERSION);
|
|
vers_id = WINBINDD_CACHE_VERSION;
|
|
}
|
|
|
|
if (vers_id != WINBINDD_CACHE_VERSION) {
|
|
if (vers_id == WINBINDD_CACHE_VER1) {
|
|
ok = wbcache_upgrade_v1_to_v2(tdb);
|
|
if (!ok) {
|
|
DBG_DEBUG("winbindd_validate_cache: upgrade to version 2 failed.\n");
|
|
unlink(tdb_path);
|
|
goto done;
|
|
}
|
|
|
|
tdb_store_uint32(tdb,
|
|
WINBINDD_CACHE_VERSION_KEYSTR,
|
|
WINBINDD_CACHE_VERSION);
|
|
vers_id = WINBINDD_CACHE_VER2;
|
|
}
|
|
}
|
|
|
|
tdb_close(tdb);
|
|
|
|
ret = tdb_validate_and_backup(tdb_path, cache_traverse_validate_fn);
|
|
|
|
if (ret != 0) {
|
|
DBG_DEBUG("winbindd_validate_cache: validation not successful.\n"
|
|
"removing tdb %s.\n", tdb_path);
|
|
unlink(tdb_path);
|
|
}
|
|
|
|
done:
|
|
TALLOC_FREE(tdb_path);
|
|
DBG_DEBUG("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;
|
|
char *tdb_path;
|
|
|
|
DBG_DEBUG("winbindd_validate_cache: replacing panic function\n");
|
|
smb_panic_fn = validate_panic;
|
|
|
|
tdb_path = wcache_path();
|
|
if (tdb_path == NULL) {
|
|
goto err_panic_restore;
|
|
}
|
|
|
|
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) {
|
|
DBG_DEBUG("winbindd_validate_cache_nobackup: validation not "
|
|
"successful.\n");
|
|
}
|
|
|
|
TALLOC_FREE(tdb_path);
|
|
err_panic_restore:
|
|
DBG_DEBUG("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) {
|
|
DBG_ERR("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 i, idx;
|
|
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 ) ) {
|
|
DBG_DEBUG("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( *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);
|
|
if (list[idx].domain_name == NULL) {
|
|
return false;
|
|
}
|
|
if (new_dom->alt_name != NULL) {
|
|
list[idx].dns_name = talloc_strdup(list, new_dom->alt_name);
|
|
if (list[idx].dns_name == NULL) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ( !is_null_sid( &new_dom->sid ) ) {
|
|
sid_copy( &list[idx].sid, &new_dom->sid );
|
|
} else {
|
|
sid_copy(&list[idx].sid, &global_sid_NULL);
|
|
}
|
|
|
|
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 ) {
|
|
DBG_INFO("make_tdc_key: Keyname workgroup is NULL!\n");
|
|
return key;
|
|
}
|
|
|
|
if (asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name ) == -1) {
|
|
return key;
|
|
}
|
|
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;
|
|
size_t i = 0;
|
|
|
|
DBG_DEBUG("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 ? buffer+len : NULL,
|
|
buffer ? buflen-len : 0, "d",
|
|
num_domains );
|
|
|
|
/* now pack each domain trust record */
|
|
for ( i=0; i<num_domains; i++ ) {
|
|
|
|
struct dom_sid_buf tmp;
|
|
|
|
if ( buflen > 0 ) {
|
|
DBG_DEBUG("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 ? buffer+len : NULL,
|
|
buffer ? buflen-len : 0, "fffddd",
|
|
domains[i].domain_name,
|
|
domains[i].dns_name ? domains[i].dns_name : "",
|
|
dom_sid_str_buf(&domains[i].sid, &tmp),
|
|
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 ) {
|
|
DBG_ERR("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_t 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 ) {
|
|
DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
|
|
return 0;
|
|
}
|
|
|
|
list = talloc_array( NULL, struct winbindd_tdc_domain, num_domains );
|
|
if ( !list ) {
|
|
DBG_ERR("unpack_tdc_domains: Failed to talloc() domain list!\n");
|
|
return 0;
|
|
}
|
|
|
|
for ( i=0; i<num_domains; i++ ) {
|
|
int this_len;
|
|
|
|
this_len = tdb_unpack( buf+len, buflen-len, "fffddd",
|
|
domain_name,
|
|
dns_name,
|
|
sid_string,
|
|
&flags,
|
|
&attribs,
|
|
&type );
|
|
|
|
if ( this_len == -1 ) {
|
|
DBG_INFO("unpack_tdc_domains: Failed to unpack domain array\n");
|
|
TALLOC_FREE( list );
|
|
return 0;
|
|
}
|
|
len += this_len;
|
|
|
|
DBG_DEBUG("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 = NULL;
|
|
if (dns_name[0] != '\0') {
|
|
list[i].dns_name = talloc_strdup(list, dns_name);
|
|
}
|
|
if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
|
|
DBG_DEBUG("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, TDB_REPLACE);
|
|
|
|
done:
|
|
SAFE_FREE( data.dptr );
|
|
SAFE_FREE( key.dptr );
|
|
|
|
return ( ret == 0 );
|
|
}
|
|
|
|
/*********************************************************************
|
|
********************************************************************/
|
|
|
|
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;
|
|
struct dom_sid_buf buf;
|
|
|
|
DBG_DEBUG("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,
|
|
dom_sid_str_buf(&domain->sid, &buf),
|
|
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;
|
|
}
|
|
|
|
static struct winbindd_tdc_domain *wcache_tdc_dup_domain(
|
|
TALLOC_CTX *mem_ctx, const struct winbindd_tdc_domain *src)
|
|
{
|
|
struct winbindd_tdc_domain *dst;
|
|
|
|
dst = talloc(mem_ctx, struct winbindd_tdc_domain);
|
|
if (dst == NULL) {
|
|
goto fail;
|
|
}
|
|
dst->domain_name = talloc_strdup(dst, src->domain_name);
|
|
if (dst->domain_name == NULL) {
|
|
goto fail;
|
|
}
|
|
|
|
dst->dns_name = NULL;
|
|
if (src->dns_name != NULL) {
|
|
dst->dns_name = talloc_strdup(dst, src->dns_name);
|
|
if (dst->dns_name == NULL) {
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
sid_copy(&dst->sid, &src->sid);
|
|
dst->trust_flags = src->trust_flags;
|
|
dst->trust_type = src->trust_type;
|
|
dst->trust_attribs = src->trust_attribs;
|
|
return dst;
|
|
fail:
|
|
TALLOC_FREE(dst);
|
|
return NULL;
|
|
}
|
|
|
|
/*********************************************************************
|
|
********************************************************************/
|
|
|
|
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;
|
|
size_t i;
|
|
struct winbindd_tdc_domain *d = NULL;
|
|
|
|
DBG_DEBUG("wcache_tdc_fetch_domain: Searching for domain %s\n", name);
|
|
|
|
if ( !init_wcache() ) {
|
|
return NULL;
|
|
}
|
|
|
|
/* 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) )
|
|
{
|
|
DBG_DEBUG("wcache_tdc_fetch_domain: Found domain %s\n",
|
|
name);
|
|
|
|
d = wcache_tdc_dup_domain(ctx, &dom_list[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
TALLOC_FREE( dom_list );
|
|
|
|
return d;
|
|
}
|
|
|
|
/*********************************************************************
|
|
********************************************************************/
|
|
|
|
void wcache_tdc_clear( void )
|
|
{
|
|
if ( !init_wcache() )
|
|
return;
|
|
|
|
wcache_tdc_store_list( NULL, 0 );
|
|
|
|
return;
|
|
}
|
|
|
|
static bool wcache_ndr_key(TALLOC_CTX *mem_ctx, const char *domain_name,
|
|
uint32_t opnum, const DATA_BLOB *req,
|
|
TDB_DATA *pkey)
|
|
{
|
|
char *key;
|
|
size_t keylen;
|
|
|
|
key = talloc_asprintf(mem_ctx, "NDR/%s/%d/", domain_name, (int)opnum);
|
|
if (key == NULL) {
|
|
return false;
|
|
}
|
|
keylen = talloc_get_size(key) - 1;
|
|
|
|
key = talloc_realloc(mem_ctx, key, char, keylen + req->length);
|
|
if (key == NULL) {
|
|
return false;
|
|
}
|
|
memcpy(key + keylen, req->data, req->length);
|
|
|
|
pkey->dptr = (uint8_t *)key;
|
|
pkey->dsize = talloc_get_size(key);
|
|
return true;
|
|
}
|
|
|
|
static bool wcache_opnum_cacheable(uint32_t opnum)
|
|
{
|
|
switch (opnum) {
|
|
case NDR_WBINT_LOOKUPSID:
|
|
case NDR_WBINT_LOOKUPSIDS:
|
|
case NDR_WBINT_LOOKUPNAME:
|
|
case NDR_WBINT_SIDS2UNIXIDS:
|
|
case NDR_WBINT_UNIXIDS2SIDS:
|
|
case NDR_WBINT_GETNSSINFO:
|
|
case NDR_WBINT_LOOKUPUSERALIASES:
|
|
case NDR_WBINT_LOOKUPUSERGROUPS:
|
|
case NDR_WBINT_LOOKUPGROUPMEMBERS:
|
|
case NDR_WBINT_QUERYGROUPLIST:
|
|
case NDR_WBINT_QUERYUSERRIDLIST:
|
|
case NDR_WBINT_DSGETDCNAME:
|
|
case NDR_WBINT_LOOKUPRIDS:
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool wcache_fetch_ndr(TALLOC_CTX *mem_ctx, struct winbindd_domain *domain,
|
|
uint32_t opnum, const DATA_BLOB *req, DATA_BLOB *resp)
|
|
{
|
|
TDB_DATA key, data;
|
|
bool ret = false;
|
|
|
|
if (!wcache_opnum_cacheable(opnum) ||
|
|
is_my_own_sam_domain(domain) ||
|
|
is_builtin_domain(domain)) {
|
|
return false;
|
|
}
|
|
|
|
if (wcache->tdb == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
|
|
return false;
|
|
}
|
|
data = tdb_fetch(wcache->tdb, key);
|
|
TALLOC_FREE(key.dptr);
|
|
|
|
if (data.dptr == NULL) {
|
|
return false;
|
|
}
|
|
if (data.dsize < 12) {
|
|
goto fail;
|
|
}
|
|
|
|
if (is_domain_online(domain)) {
|
|
uint32_t entry_seqnum, dom_seqnum, last_check;
|
|
uint64_t entry_timeout;
|
|
|
|
if (!wcache_fetch_seqnum(domain->name, &dom_seqnum,
|
|
&last_check)) {
|
|
goto fail;
|
|
}
|
|
entry_seqnum = IVAL(data.dptr, 0);
|
|
if (entry_seqnum != dom_seqnum) {
|
|
DBG_DEBUG("Entry has wrong sequence number: %d\n",
|
|
(int)entry_seqnum);
|
|
goto fail;
|
|
}
|
|
entry_timeout = BVAL(data.dptr, 4);
|
|
if (time(NULL) > (time_t)entry_timeout) {
|
|
DBG_DEBUG("Entry has timed out\n");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
resp->data = (uint8_t *)talloc_memdup(mem_ctx, data.dptr + 12,
|
|
data.dsize - 12);
|
|
if (resp->data == NULL) {
|
|
DBG_DEBUG("talloc failed\n");
|
|
goto fail;
|
|
}
|
|
resp->length = data.dsize - 12;
|
|
|
|
ret = true;
|
|
fail:
|
|
SAFE_FREE(data.dptr);
|
|
return ret;
|
|
}
|
|
|
|
void wcache_store_ndr(struct winbindd_domain *domain, uint32_t opnum,
|
|
const DATA_BLOB *req, const DATA_BLOB *resp)
|
|
{
|
|
TDB_DATA key, data;
|
|
uint32_t dom_seqnum, last_check;
|
|
uint64_t timeout;
|
|
|
|
if (!wcache_opnum_cacheable(opnum) ||
|
|
is_my_own_sam_domain(domain) ||
|
|
is_builtin_domain(domain)) {
|
|
return;
|
|
}
|
|
|
|
if (wcache->tdb == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (!wcache_fetch_seqnum(domain->name, &dom_seqnum, &last_check)) {
|
|
DBG_DEBUG("could not fetch seqnum for domain %s\n",
|
|
domain->name);
|
|
return;
|
|
}
|
|
|
|
if (!wcache_ndr_key(talloc_tos(), domain->name, opnum, req, &key)) {
|
|
return;
|
|
}
|
|
|
|
timeout = time(NULL) + lp_winbind_cache_time();
|
|
|
|
data.dsize = resp->length + 12;
|
|
data.dptr = talloc_array(key.dptr, uint8_t, data.dsize);
|
|
if (data.dptr == NULL) {
|
|
goto done;
|
|
}
|
|
|
|
SIVAL(data.dptr, 0, dom_seqnum);
|
|
SBVAL(data.dptr, 4, timeout);
|
|
memcpy(data.dptr + 12, resp->data, resp->length);
|
|
|
|
tdb_store(wcache->tdb, key, data, TDB_REPLACE);
|
|
|
|
done:
|
|
TALLOC_FREE(key.dptr);
|
|
return;
|
|
}
|