Winbind cache backend functions
Copyright (C) Andrew Tridgell 2001
- Copyright (C) Gerald Carter 2003
+ 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
if (!centry_check_bytes(centry, 4)) {
smb_panic_fn("centry_uint32");
- return (uint32)-1;
}
ret = IVAL(centry->data, centry->ofs);
centry->ofs += 4;
uint16 ret;
if (!centry_check_bytes(centry, 2)) {
smb_panic_fn("centry_uint16");
- return (uint16)-1;
}
ret = CVAL(centry->data, centry->ofs);
centry->ofs += 2;
uint8 ret;
if (!centry_check_bytes(centry, 1)) {
smb_panic_fn("centry_uint8");
- return (uint8)-1;
}
ret = CVAL(centry->data, centry->ofs);
centry->ofs += 1;
NTTIME ret;
if (!centry_check_bytes(centry, 8)) {
smb_panic_fn("centry_nttime");
- return (NTTIME)-1;
}
ret = IVAL(centry->data, centry->ofs);
centry->ofs += 4;
if (!centry_check_bytes(centry, (size_t)len)) {
smb_panic_fn("centry_string");
- return NULL;
}
ret = TALLOC_ARRAY(mem_ctx, char, len+1);
if (!ret) {
smb_panic_fn("centry_string out of memory\n");
- return NULL;
}
memcpy(ret,centry->data + centry->ofs, len);
ret[len] = 0;
ret = TALLOC_ARRAY(mem_ctx, char, 16);
if (!ret) {
smb_panic_fn("centry_hash out of memory\n");
- return NULL;
}
memcpy(ret,centry->data + centry->ofs, 16);
centry->ofs += 16;
time_t t = time(NULL);
unsigned cache_time = lp_winbind_cache_time();
+ if ( IS_DOMAIN_OFFLINE(domain) ) {
+ return;
+ }
+
get_cache( domain );
#if 0 /* JERRY -- disable as the default cache time is now 5 minutes */
goto done;
/* important! make sure that we know if this is a native
- mode domain or not */
+ mode domain or not. And that we can contact it. */
+
+ if ( winbindd_can_contact_domain( domain ) ) {
+ status = domain->backend->sequence_number(domain,
+ &domain->sequence_number);
+ } else {
+ /* just use the current time */
+ status = NT_STATUS_OK;
+ domain->sequence_number = time(NULL);
+ }
- status = domain->backend->sequence_number(domain, &domain->sequence_number);
/* the above call could have set our domain->backend to NULL when
* coming from offline to online mode, make sure to reinitialize the
fstrcpy(uname, name);
strupper_m(uname);
centry_end(centry, "NS/%s/%s", domain_name, uname);
- DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s\n", domain_name, uname,
- sid_string_static(sid)));
+ DEBUG(10,("wcache_save_name_to_sid: %s\\%s -> %s (%s)\n", domain_name, uname,
+ sid_string_static(sid), nt_errstr(status)));
centry_free(centry);
}
centry_put_string(centry, name);
}
centry_end(centry, "SN/%s", sid_to_string(sid_string, sid));
- DEBUG(10,("wcache_save_sid_to_name: %s -> %s\n", sid_string, name));
+ DEBUG(10,("wcache_save_sid_to_name: %s -> %s (%s)\n", sid_string,
+ name, nt_errstr(status)));
centry_free(centry);
}
}
static void wcache_save_password_policy(struct winbindd_domain *domain, NTSTATUS status, SAM_UNK_INFO_1 *policy)
- {
+{
struct cache_entry *centry;
centry = centry_start(domain, status);
(*info) = TALLOC_ARRAY(mem_ctx, WINBIND_USERINFO, *num_entries);
if (! (*info)) {
smb_panic_fn("query_user_list out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
for (i=0; i<(*num_entries); i++) {
(*info)[i].acct_name = centry_string(centry, mem_ctx);
(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
if (! (*info)) {
smb_panic_fn("enum_dom_groups out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
for (i=0; i<(*num_entries); i++) {
fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
(*info) = TALLOC_ARRAY(mem_ctx, struct acct_info, *num_entries);
if (! (*info)) {
smb_panic_fn("enum_dom_groups out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
for (i=0; i<(*num_entries); i++) {
fstrcpy((*info)[i].acct_name, centry_string(centry, mem_ctx));
(*user_gids) = TALLOC_ARRAY(mem_ctx, DOM_SID, *num_groups);
if (! (*user_gids)) {
smb_panic_fn("lookup_usergroups out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
for (i=0; i<(*num_groups); i++) {
centry_sid(centry, mem_ctx, &(*user_gids)[i]);
status = domain->backend->lookup_usergroups(domain, mem_ctx, user_sid, num_groups, user_gids);
+ if ( NT_STATUS_EQUAL(status, NT_STATUS_SYNCHRONIZATION_REQUIRED) )
+ goto skip_save;
+
/* and save it */
refresh_sequence_number(domain, False);
centry = centry_start(domain, status);
if (! (*sid_mem) || ! (*names) || ! (*name_types)) {
smb_panic_fn("lookup_groupmem out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
for (i=0; i<(*num_names); i++) {
if (! (*dom_sids) || ! (*names) || ! (*alt_names)) {
smb_panic_fn("trusted_domains out of memory");
- centry_free(centry);
- return NT_STATUS_NO_MEMORY;
}
} else {
(*names) = NULL;
status = NT_STATUS_OK;
}
+
+#if 0 /* Disabled as we want the trust dom list to be managed by
+ the main parent and always to make the query. --jerry */
+
/* and save it */
refresh_sequence_number(domain, False);
centry_free(centry);
skip_save:
+#endif
+
return status;
}
}
}
-static BOOL init_wcache(void)
+BOOL init_wcache(void)
{
if (wcache == NULL) {
wcache = SMB_XMALLOC_P(struct winbind_cache);
fstr_sprintf(key_str, "DE/%d", pid);
if (tdb_store(wcache->tdb, string_tdb_data(key_str),
- make_tdb_data(response->extra_data.data,
+ make_tdb_data((uint8 *)response->extra_data.data,
response->length - sizeof(*response)),
TDB_REPLACE) == 0)
return;
struct cache_entry *centry = NULL;
NTSTATUS status;
fstring uname;
+ BOOL original_online_state;
domain = find_lookup_domain_from_name(domain_name);
if (domain == NULL) {
fstrcpy(uname, name);
strupper_m(uname);
+ /* If we are doing a cached logon, temporarily set the domain
+ offline so the cache won't expire the entry */
+
+ original_online_state = domain->online;
+ domain->online = False;
centry = wcache_fetch(cache, domain, "NS/%s/%s", domain_name, uname);
+ domain->online = original_online_state;
+
if (centry == NULL) {
return False;
}
Validate functions for all possible cache tdb keys.
***********************************************************************/
-static BOOL bad_cache_entry;
+struct validation_status {
+ BOOL tdb_error;
+ BOOL bad_freelist;
+ BOOL bad_entry;
+ BOOL unknown_key;
+ BOOL success;
+};
-static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data)
+static struct cache_entry *create_centry_validate(const char *kstr, TDB_DATA data,
+ struct validation_status *state)
{
struct cache_entry *centry;
if (centry->len < 8) {
/* huh? corrupt cache? */
- DEBUG(0,("validate_create_centry: Corrupt cache for key %s (len < 8) ?\n", kstr));
+ DEBUG(0,("create_centry_validate: Corrupt cache for key %s (len < 8) ?\n", kstr));
centry_free(centry);
- bad_cache_entry = True;
+ state->bad_entry = True;
+ state->success = False;
return NULL;
}
return centry;
}
-static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_seqnum(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
if (dbuf.dsize != 8) {
DEBUG(0,("validate_seqnum: Corrupt cache for key %s (len %u != 8) ?\n",
keystr, (unsigned int)dbuf.dsize ));
- bad_cache_entry = True;
+ state->bad_entry = True;
return 1;
}
return 0;
}
-static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ns(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
if (!centry) {
return 1;
}
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_ns: %s ok\n", keystr));
return 0;
}
-static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_sn(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
if (!centry) {
return 1;
}
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_sn: %s ok\n", keystr));
return 0;
}
-static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
DOM_SID sid;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_u: %s ok\n", keystr));
return 0;
}
-static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_loc_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
if (!centry) {
return 1;
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_loc_pol: %s ok\n", keystr));
return 0;
}
-static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_pwd_pol(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
if (!centry) {
return 1;
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_pwd_pol: %s ok\n", keystr));
return 0;
}
-static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_cred(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
if (!centry) {
return 1;
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_cred: %s ok\n", keystr));
return 0;
}
-static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ul(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_entries, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_ul: %s ok\n", keystr));
return 0;
}
-static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_gl(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_entries, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_gl: %s ok\n", keystr));
return 0;
}
-static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ug(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_groups, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_ug: %s ok\n", keystr));
return 0;
}
-static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_ua(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_aliases, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_ua: %s ok\n", keystr));
return 0;
}
-static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_gm(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_names, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_gm: %s ok\n", keystr));
return 0;
}
-static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_dr(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
/* Can't say anything about this other than must be nonzero. */
if (dbuf.dsize == 0) {
DEBUG(0,("validate_dr: Corrupt cache for key %s (len == 0) ?\n",
keystr));
- bad_cache_entry = True;
+ state->bad_entry = True;
+ state->success = False;
return 1;
}
return 0;
}
-static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_de(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
/* Can't say anything about this other than must be nonzero. */
if (dbuf.dsize == 0) {
DEBUG(0,("validate_de: Corrupt cache for key %s (len == 0) ?\n",
keystr));
- bad_cache_entry = True;
+ state->bad_entry = True;
+ state->success = False;
return 1;
}
return 0;
}
-static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_trustdoms(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
- struct cache_entry *centry = create_centry_validate(keystr, dbuf);
+ struct cache_entry *centry = create_centry_validate(keystr, dbuf, state);
int32 num_domains, i;
if (!centry) {
centry_free(centry);
- if (bad_cache_entry) {
+ if (!(state->success)) {
return 1;
}
DEBUG(10,("validate_trustdoms: %s ok\n", keystr));
return 0;
}
-static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf)
+static int validate_trustdomcache(TALLOC_CTX *mem_ctx, const char *keystr,
+ TDB_DATA dbuf,
+ struct validation_status *state)
+{
+ if (dbuf.dsize == 0) {
+ DEBUG(0, ("validate_trustdomcache: Corrupt cache for "
+ "key %s (len ==0) ?\n", keystr));
+ state->bad_entry = True;
+ state->success = False;
+ return 1;
+ }
+
+ DEBUG(10, ("validate_trustdomcache: %s ok\n", keystr));
+ DEBUGADD(10, (" Don't trust me, I am a DUMMY!\n"));
+ return 0;
+}
+
+static int validate_offline(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
{
if (dbuf.dsize != 4) {
DEBUG(0,("validate_offline: Corrupt cache for key %s (len %u != 4) ?\n",
keystr, (unsigned int)dbuf.dsize ));
- bad_cache_entry = True;
+ state->bad_entry = True;
+ state->success = False;
return 1;
}
DEBUG(10,("validate_offline: %s ok\n", keystr));
return 0;
}
+static int validate_cache_version(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
+ struct validation_status *state)
+{
+ if (dbuf.dsize != 4) {
+ DEBUG(0, ("validate_cache_version: Corrupt cache for "
+ "key %s (len %u != 4) ?\n",
+ keystr, (unsigned int)dbuf.dsize));
+ state->bad_entry = True;
+ state->success = False;
+ return 1;
+ }
+
+ DEBUG(10, ("validate_cache_version: %s ok\n", keystr));
+ return 0;
+}
+
/***********************************************************************
A list of all possible cache tdb keys with associated validation
functions.
struct key_val_struct {
const char *keyname;
- int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf);
+ int (*validate_data_fn)(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf, struct validation_status* state);
} key_val[] = {
{"SEQNUM/", validate_seqnum},
{"NS/", validate_ns},
{"DR/", validate_dr},
{"DE/", validate_de},
{"TRUSTDOMS/", validate_trustdoms},
+ {"TRUSTDOMCACHE/", validate_trustdomcache},
{"WINBINDD_OFFLINE", validate_offline},
+ {WINBINDD_CACHE_VERSION_KEYSTR, validate_cache_version},
{NULL, NULL}
};
static int cache_traverse_validate_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state)
{
int i;
+ struct validation_status *v_state = (struct validation_status *)state;
/* Paranoia check. */
if (kbuf.dsize > 1024) {
char *keystr;
int ret;
- keystr = SMB_MALLOC(kbuf.dsize+1);
+ keystr = SMB_MALLOC_ARRAY(char, kbuf.dsize+1);
if (!keystr) {
return 1;
}
return 1;
}
- ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf);
+ ret = key_val[i].validate_data_fn(mem_ctx, keystr, dbuf,
+ v_state);
SAFE_FREE(keystr);
talloc_destroy(mem_ctx);
dump_data(0, (uint8 *)kbuf.dptr, kbuf.dsize);
DEBUG(0,("data :\n"));
dump_data(0, (uint8 *)dbuf.dptr, dbuf.dsize);
+ v_state->unknown_key = True;
+ v_state->success = False;
return 1; /* terminate. */
}
static void validate_panic(const char *const why)
{
- DEBUG(0,("validating cache: would panic %s\n", why ));
- bad_cache_entry = True;
-}
-
-/* Handle any signals generated when validating a possibly
- bad cache tdb. */
-
-static jmp_buf jmpbuf;
-
-#ifdef SIGSEGV
-static void sig_segv(int sig)
-{
- longjmp(jmpbuf, SIGSEGV);
+ DEBUG(0,("validating cache: would panic %s\n", why ));
+ DEBUGADD(0, ("exiting instead (cache validation mode)\n"));
+ exit(47);
}
-#endif
-
-#ifdef SIGBUS
-static void sig_bus(int sig)
-{
- longjmp(jmpbuf, SIGBUS);
-}
-#endif
-
-#ifdef SIGABRT
-static void sig_abrt(int sig)
-{
- longjmp(jmpbuf, SIGABRT);
-}
-#endif
/***********************************************************************
Try and validate every entry in the winbindd cache. If we fail here,
function) will restart us as we don't know if we crashed or not.
***********************************************************************/
-int winbindd_validate_cache(void)
+/*
+ * internal validation function, executed by the child.
+ */
+static int winbindd_validate_cache_child(const char *cache_path, int pfd)
{
- BOOL ret = -1;
- int fd = -1;
+ int ret = -1;
+ int tfd = -1;
int num_entries = 0;
TDB_CONTEXT *tdb = NULL;
- const char *cache_path = lock_path("winbindd_cache.tdb");
-
-#ifdef SIGSEGV
- void (*old_segv_handler)(int) = CatchSignal(SIGSEGV,SIGNAL_CAST sig_segv);
-#endif
-#ifdef SIGBUS
- void (*old_bus_handler)(int) = CatchSignal(SIGBUS,SIGNAL_CAST sig_bus);
-#endif
-#ifdef SIGABRT
- void (*old_abrt_handler)(int) = CatchSignal(SIGABRT,SIGNAL_CAST sig_abrt);
-#endif
-
- switch((ret = setjmp(jmpbuf))) {
- case 0:
- ret = -1;
- break;
- case SIGSEGV:
- case SIGBUS:
- case SIGABRT:
- default:
- goto out;
- }
-
- /* Doh ! Volker is very smart :-). Use TDB_NOMMAP to prevent
- * any wild pointer references when reading a corrupt tdb file. */
+ struct validation_status v_status;
+
+ v_status.tdb_error = False;
+ v_status.bad_freelist = False;
+ v_status.bad_entry = False;
+ v_status.unknown_key = False;
+ v_status.success = True;
tdb = tdb_open_log(cache_path,
WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
- lp_winbind_offline_logon() ? TDB_NOMMAP : (TDB_NOMMAP | TDB_CLEAR_IF_FIRST),
+ lp_winbind_offline_logon()
+ ? TDB_DEFAULT
+ : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST),
O_RDWR|O_CREAT, 0600);
if (!tdb) {
+ v_status.tdb_error = True;
+ v_status.success = False;
goto out;
}
- fd = tdb_fd(tdb);
+ tfd = tdb_fd(tdb);
/* Check the cache freelist is good. */
if (tdb_validate_freelist(tdb, &num_entries) == -1) {
- DEBUG(0,("winbindd_validate_cache: bad freelist in cache %s\n",
+ DEBUG(0,("winbindd_validate_cache_child: bad freelist in cache %s\n",
cache_path));
+ v_status.bad_freelist = True;
+ v_status.success = False;
goto out;
}
- DEBUG(10,("winbindd_validate_cache: cache %s freelist has %d entries\n",
+ DEBUG(10,("winbindd_validate_cache_child: cache %s freelist has %d entries\n",
cache_path, num_entries));
- smb_panic_fn = validate_panic;
-
/* Now traverse the cache to validate it. */
- num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, NULL);
- if (num_entries == -1 || bad_cache_entry) {
- DEBUG(0,("winbindd_validate_cache: cache %s traverse failed\n",
+ num_entries = tdb_traverse(tdb, cache_traverse_validate_fn, (void *)&v_status);
+ if (num_entries == -1 || !(v_status.success)) {
+ DEBUG(0,("winbindd_validate_cache_child: cache %s traverse failed\n",
cache_path));
+ if (!(v_status.success)) {
+ if (v_status.bad_entry) {
+ DEBUGADD(0, (" -> bad entry found\n"));
+ }
+ if (v_status.unknown_key) {
+ DEBUGADD(0, (" -> unknown key encountered\n"));
+ }
+ }
goto out;
}
- DEBUG(10,("winbindd_validate_cache: cache %s is good "
+ DEBUG(10,("winbindd_validate_cache_child: cache %s is good "
"with %d entries\n", cache_path, num_entries));
ret = 0; /* Cache is good. */
- out:
-
- bad_cache_entry = False;
- smb_panic_fn = smb_panic;
-
- /* Ensure if we segv on exit we use the original
- handlers to avoid a loop. */
-
-#ifdef SIGSEGV
- CatchSignal(SIGSEGV,SIGNAL_CAST old_segv_handler);
-#endif
-#ifdef SIGBUS
- CatchSignal(SIGBUS,SIGNAL_CAST old_bus_handler);
-#endif
-#ifdef SIGABRT
- CatchSignal(SIGABRT,SIGNAL_CAST old_abrt_handler);
-#endif
-
+out:
if (tdb) {
if (ret == 0) {
tdb_close(tdb);
- } else if (fd != -1) {
- close(fd);
+ }
+ else if (tfd != -1) {
+ close(tfd);
}
}
- if (ret) {
+ DEBUG(10, ("winbindd_validate_cache_child: writing status to pipe\n"));
+ write (pfd, (const char *)&v_status, sizeof(v_status));
+ close(pfd);
+
+ return ret;
+}
+
+int winbindd_validate_cache(void)
+{
+ pid_t child_pid = -1;
+ int child_status = 0;
+ int wait_pid = 0;
+ int ret = -1;
+ int pipe_fds[2];
+ struct validation_status v_status;
+ int bytes_read = 0;
+ const char *cache_path = lock_path("winbindd_cache.tdb");
+
+ DEBUG(10, ("winbindd_validate_cache: replacing panic function\n"));
+ smb_panic_fn = validate_panic;
+
+ /* fork and let the child do the validation.
+ * benefit: no need to twist signal handlers and panic functions.
+ * just let the child panic. we catch the signal.
+ * communicate the extended status struct over a pipe. */
+
+ if (pipe(pipe_fds) != 0) {
+ DEBUG(0, ("winbindd_validate_cache: unable to create pipe, "
+ "error %s", strerror(errno)));
+ smb_panic("winbind_validate_cache: unable to create pipe.");
+ }
+
+ DEBUG(10, ("winbindd_validate_cache: forking to let child do validation.\n"));
+ child_pid = sys_fork();
+ if (child_pid == 0) {
+ DEBUG(10, ("winbindd_validate_cache (validation child): created\n"));
+ close(pipe_fds[0]); /* close reading fd */
+ DEBUG(10, ("winbindd_validate_cache (validation child): "
+ "calling winbindd_validate_cache_child\n"));
+ exit(winbindd_validate_cache_child(cache_path, pipe_fds[1]));
+ }
+ else if (child_pid < 0) {
+ smb_panic("winbindd_validate_cache: fork for validation failed.");
+ }
+
+ /* parent */
+
+ DEBUG(10, ("winbindd_validate_cache: fork succeeded, child PID = %d\n",
+ child_pid));
+ close(pipe_fds[1]); /* close writing fd */
+
+ v_status.success = True;
+ v_status.bad_entry = False;
+ v_status.unknown_key = False;
+
+ DEBUG(10, ("winbindd_validate_cache: reading from pipe.\n"));
+ bytes_read = read(pipe_fds[0], (void *)&v_status, sizeof(v_status));
+ close(pipe_fds[0]);
+
+ if (bytes_read != sizeof(v_status)) {
+ DEBUG(10, ("winbindd_validate_cache: read %d bytes from pipe "
+ "but expected %d", bytes_read, sizeof(v_status)));
+ DEBUGADD(10, (" -> assuming child crashed\n"));
+ v_status.success = False;
+ }
+ else {
+ DEBUG(10, ("winbindd_validate_cache: read status from child\n"));
+ DEBUGADD(10, (" * tdb error: %s\n", v_status.tdb_error ? "yes" : "no"));
+ DEBUGADD(10, (" * bad freelist: %s\n", v_status.bad_freelist ? "yes" : "no"));
+ DEBUGADD(10, (" * bad entry: %s\n", v_status.bad_entry ? "yes" : "no"));
+ DEBUGADD(10, (" * unknown key: %s\n", v_status.unknown_key ? "yes" : "no"));
+ DEBUGADD(10, (" => overall success: %s\n", v_status.success ? "yes" : "no"));
+ }
+
+ if (!v_status.success) {
+ DEBUG(10, ("winbindd_validate_cache: validation not successful.\n"));
+ DEBUGADD(10, ("removing tdb %s.\n", cache_path));
unlink(cache_path);
}
+ DEBUG(10, ("winbindd_validate_cache: waiting for child to finish...\n"));
+ while ((wait_pid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
+ if (errno == EINTR) {
+ DEBUG(10, ("winbindd_validate_cache: got signal during "
+ "waitpid, retrying\n"));
+ errno = 0;
+ continue;
+ }
+ DEBUG(0, ("winbindd_validate_cache: waitpid failed with "
+ "errno %s\n", strerror(errno)));
+ smb_panic("winbindd_validate_cache: waitpid failed.");
+ }
+ if (wait_pid != child_pid) {
+ DEBUG(0, ("winbindd_validate_cache: waitpid returned pid %d, "
+ "but %d was expexted\n", wait_pid, child_pid));
+ smb_panic("winbindd_validate_cache: waitpid returned "
+ "unexpected PID.");
+ }
+
+
+ DEBUG(10, ("winbindd_validate_cache: validating child returned.\n"));
+ if (WIFEXITED(child_status)) {
+ DEBUG(10, ("winbindd_validate_cache: child exited, code %d.\n",
+ WEXITSTATUS(child_status)));
+ ret = WEXITSTATUS(child_status);
+ }
+ if (WIFSIGNALED(child_status)) {
+ DEBUG(10, ("winbindd_validate_cache: child terminated "
+ "by signal %d\n", WTERMSIG(child_status)));
+#ifdef WCOREDUMP
+ if (WCOREDUMP(child_status)) {
+ DEBUGADD(10, ("core dumped\n"));
+ }
+#endif
+ ret = WTERMSIG(child_status);
+ }
+ if (WIFSTOPPED(child_status)) {
+ DEBUG(10, ("winbindd_validate_cache: child was stopped "
+ "by signal %d\n",
+ WSTOPSIG(child_status)));
+ ret = WSTOPSIG(child_status);
+ }
+
+ DEBUG(10, ("winbindd_validate_cache: restoring panic function\n"));
+ smb_panic_fn = smb_panic;
return ret;
}
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL add_wbdomain_to_tdc_array( struct winbindd_domain *new_dom,
+ struct winbindd_tdc_domain **domains,
+ size_t *num_domains )
+{
+ struct winbindd_tdc_domain *list = NULL;
+ size_t idx;
+ int i;
+ BOOL set_only = False;
+
+ /* don't allow duplicates */
+
+ idx = *num_domains;
+ list = *domains;
+
+ for ( i=0; i< (*num_domains); i++ ) {
+ if ( strequal( new_dom->name, list[i].domain_name ) ) {
+ DEBUG(10,("add_wbdomain_to_tdc_array: Found existing record for %s\n",
+ new_dom->name));
+ idx = i;
+ set_only = True;
+
+ break;
+ }
+ }
+
+ if ( !set_only ) {
+ if ( !*domains ) {
+ list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, 1 );
+ idx = 0;
+ } else {
+ list = TALLOC_REALLOC_ARRAY( *domains, *domains,
+ struct winbindd_tdc_domain,
+ (*num_domains)+1);
+ idx = *num_domains;
+ }
+
+ ZERO_STRUCT( list[idx] );
+ }
+
+ if ( !list )
+ return False;
+
+ list[idx].domain_name = talloc_strdup( list, new_dom->name );
+ list[idx].dns_name = talloc_strdup( list, new_dom->alt_name );
+
+ if ( !is_null_sid( &new_dom->sid ) )
+ sid_copy( &list[idx].sid, &new_dom->sid );
+
+ if ( new_dom->domain_flags != 0x0 )
+ list[idx].trust_flags = new_dom->domain_flags;
+
+ if ( new_dom->domain_type != 0x0 )
+ list[idx].trust_type = new_dom->domain_type;
+
+ if ( new_dom->domain_trust_attribs != 0x0 )
+ list[idx].trust_attribs = new_dom->domain_trust_attribs;
+
+ if ( !set_only ) {
+ *domains = list;
+ *num_domains = idx + 1;
+ }
+
+ return True;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static TDB_DATA make_tdc_key( const char *domain_name )
+{
+ char *keystr = NULL;
+ TDB_DATA key = { NULL, 0 };
+
+ if ( !domain_name ) {
+ DEBUG(5,("make_tdc_key: Keyname workgroup is NULL!\n"));
+ return key;
+ }
+
+
+ asprintf( &keystr, "TRUSTDOMCACHE/%s", domain_name );
+ key.dptr = (unsigned char*)keystr;
+ key.dsize = strlen_m(keystr) + 1;
+
+ return key;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static int pack_tdc_domains( struct winbindd_tdc_domain *domains,
+ size_t num_domains,
+ unsigned char **buf )
+{
+ unsigned char *buffer = NULL;
+ int len = 0;
+ int buflen = 0;
+ int i = 0;
+
+ DEBUG(10,("pack_tdc_domains: Packing %d trusted domains\n",
+ (int)num_domains));
+
+ buflen = 0;
+
+ again:
+ len = 0;
+
+ /* Store the number of array items first */
+ len += tdb_pack( buffer+len, buflen-len, "d",
+ num_domains );
+
+ /* now pack each domain trust record */
+ for ( i=0; i<num_domains; i++ ) {
+
+ if ( buflen > 0 ) {
+ DEBUG(10,("pack_tdc_domains: Packing domain %s (%s)\n",
+ domains[i].domain_name,
+ domains[i].dns_name ? domains[i].dns_name : "UNKNOWN" ));
+ }
+
+ len += tdb_pack( buffer+len, buflen-len, "fffddd",
+ domains[i].domain_name,
+ domains[i].dns_name,
+ sid_string_static(&domains[i].sid),
+ domains[i].trust_flags,
+ domains[i].trust_attribs,
+ domains[i].trust_type );
+ }
+
+ if ( buflen < len ) {
+ if ( (buffer = SMB_MALLOC_ARRAY(unsigned char, len)) == NULL ) {
+ DEBUG(0,("pack_tdc_domains: failed to alloc buffer!\n"));
+ buflen = -1;
+ goto done;
+ }
+ buflen = len;
+ goto again;
+ }
+
+ *buf = buffer;
+
+ done:
+ return buflen;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static size_t unpack_tdc_domains( unsigned char *buf, int buflen,
+ struct winbindd_tdc_domain **domains )
+{
+ fstring domain_name, dns_name, sid_string;
+ uint32 type, attribs, flags;
+ int num_domains;
+ int len = 0;
+ int i;
+ struct winbindd_tdc_domain *list = NULL;
+
+ /* get the number of domains */
+ len += tdb_unpack( buf+len, buflen-len, "d", &num_domains);
+ if ( len == -1 ) {
+ DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
+ return 0;
+ }
+
+ list = TALLOC_ARRAY( NULL, struct winbindd_tdc_domain, num_domains );
+ if ( !list ) {
+ DEBUG(0,("unpack_tdc_domains: Failed to talloc() domain list!\n"));
+ return 0;
+ }
+
+ for ( i=0; i<num_domains; i++ ) {
+ len += tdb_unpack( buf+len, buflen-len, "fffddd",
+ domain_name,
+ dns_name,
+ sid_string,
+ &flags,
+ &attribs,
+ &type );
+
+ if ( len == -1 ) {
+ DEBUG(5,("unpack_tdc_domains: Failed to unpack domain array\n"));
+ TALLOC_FREE( list );
+ return 0;
+ }
+
+ DEBUG(11,("unpack_tdc_domains: Unpacking domain %s (%s) "
+ "SID %s, flags = 0x%x, attribs = 0x%x, type = 0x%x\n",
+ domain_name, dns_name, sid_string,
+ flags, attribs, type));
+
+ list[i].domain_name = talloc_strdup( list, domain_name );
+ list[i].dns_name = talloc_strdup( list, dns_name );
+ if ( !string_to_sid( &(list[i].sid), sid_string ) ) {
+ DEBUG(10,("unpack_tdc_domains: no SID for domain %s\n",
+ domain_name));
+ }
+ list[i].trust_flags = flags;
+ list[i].trust_attribs = attribs;
+ list[i].trust_type = type;
+ }
+
+ *domains = list;
+
+ return num_domains;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+static BOOL wcache_tdc_store_list( struct winbindd_tdc_domain *domains, size_t num_domains )
+{
+ TDB_DATA key = make_tdc_key( lp_workgroup() );
+ TDB_DATA data = { NULL, 0 };
+ int ret;
+
+ if ( !key.dptr )
+ return False;
+
+ /* See if we were asked to delete the cache entry */
+
+ if ( !domains ) {
+ ret = tdb_delete( wcache->tdb, key );
+ goto done;
+ }
+
+ data.dsize = pack_tdc_domains( domains, num_domains, &data.dptr );
+
+ if ( !data.dptr ) {
+ ret = -1;
+ goto done;
+ }
+
+ ret = tdb_store( wcache->tdb, key, data, 0 );
+
+ done:
+ SAFE_FREE( data.dptr );
+ SAFE_FREE( key.dptr );
+
+ return ( ret != -1 );
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_fetch_list( struct winbindd_tdc_domain **domains, size_t *num_domains )
+{
+ TDB_DATA key = make_tdc_key( lp_workgroup() );
+ TDB_DATA data = { NULL, 0 };
+
+ *domains = NULL;
+ *num_domains = 0;
+
+ if ( !key.dptr )
+ return False;
+
+ data = tdb_fetch( wcache->tdb, key );
+
+ SAFE_FREE( key.dptr );
+
+ if ( !data.dptr )
+ return False;
+
+ *num_domains = unpack_tdc_domains( data.dptr, data.dsize, domains );
+
+ SAFE_FREE( data.dptr );
+
+ if ( !*domains )
+ return False;
+
+ return True;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+BOOL wcache_tdc_add_domain( struct winbindd_domain *domain )
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ BOOL ret = False;
+
+ DEBUG(10,("wcache_tdc_add_domain: Adding domain %s (%s), SID %s, "
+ "flags = 0x%x, attributes = 0x%x, type = 0x%x\n",
+ domain->name, domain->alt_name,
+ sid_string_static(&domain->sid),
+ domain->domain_flags,
+ domain->domain_trust_attribs,
+ domain->domain_type));
+
+ if ( !init_wcache() ) {
+ return False;
+ }
+
+ /* fetch the list */
+
+ wcache_tdc_fetch_list( &dom_list, &num_domains );
+
+ /* add the new domain */
+
+ if ( !add_wbdomain_to_tdc_array( domain, &dom_list, &num_domains ) ) {
+ goto done;
+ }
+
+ /* pack the domain */
+
+ if ( !wcache_tdc_store_list( dom_list, num_domains ) ) {
+ goto done;
+ }
+
+ /* Success */
+
+ ret = True;
+ done:
+ TALLOC_FREE( dom_list );
+
+ return ret;
+}
+
+/*********************************************************************
+ ********************************************************************/
+
+struct winbindd_tdc_domain * wcache_tdc_fetch_domain( TALLOC_CTX *ctx, const char *name )
+{
+ struct winbindd_tdc_domain *dom_list = NULL;
+ size_t num_domains = 0;
+ int i;
+ struct winbindd_tdc_domain *d = NULL;
+
+ DEBUG(10,("wcache_tdc_fetch_domain: Searching for domain %s\n", name));
+
+ if ( !init_wcache() ) {
+ return False;
+ }
+
+ /* fetch the list */
+
+ wcache_tdc_fetch_list( &dom_list, &num_domains );
+
+ for ( i=0; i<num_domains; i++ ) {
+ if ( strequal(name, dom_list[i].domain_name) ||
+ strequal(name, dom_list[i].dns_name) )
+ {
+ DEBUG(10,("wcache_tdc_fetch_domain: Found domain %s\n",
+ name));
+
+ d = TALLOC_P( ctx, struct winbindd_tdc_domain );
+ if ( !d )
+ break;
+
+ d->domain_name = talloc_strdup( d, dom_list[i].domain_name );
+ d->dns_name = talloc_strdup( d, dom_list[i].dns_name );
+ sid_copy( &d->sid, &dom_list[i].sid );
+ d->trust_flags = dom_list[i].trust_flags;
+ d->trust_type = dom_list[i].trust_type;
+ d->trust_attribs = dom_list[i].trust_attribs;
+
+ break;
+ }
+ }
+
+ TALLOC_FREE( dom_list );
+
+ return d;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+void wcache_tdc_clear( void )
+{
+ if ( !init_wcache() )
+ return;
+
+ wcache_tdc_store_list( NULL, 0 );
+
+ return;
+}
+
+
+/*********************************************************************
+ ********************************************************************/
+
+static void wcache_save_user_pwinfo(struct winbindd_domain *domain,
+ NTSTATUS status,
+ const DOM_SID *user_sid,
+ const char *homedir,
+ const char *shell,
+ const char *gecos,
+ uint32 gid)
+{
+ struct cache_entry *centry;
+
+ if ( (centry = centry_start(domain, status)) == NULL )
+ return;
+
+ centry_put_string( centry, homedir );
+ centry_put_string( centry, shell );
+ centry_put_string( centry, gecos );
+ centry_put_uint32( centry, gid );
+
+ centry_end(centry, "NSS/PWINFO/%s", sid_string_static(user_sid) );
+
+ DEBUG(10,("wcache_save_user_pwinfo: %s\n", sid_string_static(user_sid) ));
+
+ centry_free(centry);
+}
+
+NTSTATUS nss_get_info_cached( struct winbindd_domain *domain,
+ const DOM_SID *user_sid,
+ TALLOC_CTX *ctx,
+ ADS_STRUCT *ads, LDAPMessage *msg,
+ char **homedir, char **shell, char **gecos,
+ gid_t *p_gid)
+{
+ struct winbind_cache *cache = get_cache(domain);
+ struct cache_entry *centry = NULL;
+ NTSTATUS nt_status;
+
+ if (!cache->tdb)
+ goto do_query;
+
+ centry = wcache_fetch(cache, domain, "NSS/PWINFO/%s", sid_string_static(user_sid));
+
+ if (!centry)
+ goto do_query;
+
+ *homedir = centry_string( centry, ctx );
+ *shell = centry_string( centry, ctx );
+ *gecos = centry_string( centry, ctx );
+ *p_gid = centry_uint32( centry );
+
+ centry_free(centry);
+
+ DEBUG(10,("nss_get_info_cached: [Cached] - user_sid %s\n",
+ sid_string_static(user_sid)));
+
+ return NT_STATUS_OK;
+
+do_query:
+
+ nt_status = nss_get_info( domain->name, user_sid, ctx, ads, msg,
+ homedir, shell, gecos, p_gid );
+
+ if ( NT_STATUS_IS_OK(nt_status) ) {
+ wcache_save_user_pwinfo( domain, nt_status, user_sid,
+ *homedir, *shell, *gecos, *p_gid );
+ }
+
+ if ( NT_STATUS_EQUAL( nt_status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND ) ) {
+ DEBUG(5,("nss_get_info_cached: Setting domain %s offline\n",
+ domain->name ));
+ set_domain_offline( domain );
+ }
+
+ return nt_status;
+}
+
+
/* the cache backend methods are exposed via this structure */
struct winbindd_methods cache_methods = {
True,