diff options
author | Gerald Carter <jerry@samba.org> | 2007-02-06 21:40:47 +0000 |
---|---|---|
committer | Gerald Carter <jerry@samba.org> | 2007-02-06 21:40:47 +0000 |
commit | 81e8924785d24bee541229337197377a39520048 (patch) | |
tree | 6d44e03de49fc82db5dc85f739c05535e05e8592 | |
parent | de9325034d501c1cb7a6199d2fdc36b54f203f3a (diff) | |
download | samba-81e8924785d24bee541229337197377a39520048.tar.gz samba-81e8924785d24bee541229337197377a39520048.tar.xz samba-81e8924785d24bee541229337197377a39520048.zip |
r21200: Grab Jeremy's winbind tdb validation code
-rw-r--r-- | source/Makefile.in | 2 | ||||
-rw-r--r-- | source/include/util_tdb.h | 5 | ||||
-rw-r--r-- | source/nsswitch/winbindd.c | 14 | ||||
-rw-r--r-- | source/nsswitch/winbindd_cache.c | 263 |
4 files changed, 282 insertions, 2 deletions
diff --git a/source/Makefile.in b/source/Makefile.in index 879f543b4f7..13719571409 100644 --- a/source/Makefile.in +++ b/source/Makefile.in @@ -188,7 +188,7 @@ MODULES = $(VFS_MODULES) $(PDB_MODULES) $(RPC_MODULES) $(IDMAP_MODULES) \ ###################################################################### TDBBASE_OBJ = tdb/common/tdb.o tdb/common/dump.o tdb/common/error.o \ - tdb/common/freelist.o tdb/common/io.o tdb/common/lock.o \ + tdb/common/freelist.o tdb/common/freelistcheck.o tdb/common/io.o tdb/common/lock.o \ tdb/common/open.o tdb/common/transaction.o tdb/common/traverse.o TDB_OBJ = $(TDBBASE_OBJ) lib/util_tdb.o tdb/common/tdbback.o diff --git a/source/include/util_tdb.h b/source/include/util_tdb.h index cb5d98fc526..a8def46e441 100644 --- a/source/include/util_tdb.h +++ b/source/include/util_tdb.h @@ -35,6 +35,11 @@ typedef struct keys_node TDB_DATA node_key; } TDB_LIST_NODE; +struct tdb_wrap { + struct tdb_context *tdb; + const char *name; + struct tdb_wrap *next, *prev; +}; TDB_LIST_NODE *tdb_search_keys(struct tdb_context*, const char*); void tdb_search_list_free(TDB_LIST_NODE*); diff --git a/source/nsswitch/winbindd.c b/source/nsswitch/winbindd.c index 1b3c037e3c3..4c6384588c6 100644 --- a/source/nsswitch/winbindd.c +++ b/source/nsswitch/winbindd.c @@ -1031,6 +1031,20 @@ int main(int argc, char **argv, char **envp) pidfile_create("winbindd"); + /* Ensure all cache and idmap caches are consistent + before we startup. */ + + if (winbindd_validate_cache()) { + /* We have a bad cache, but luckily we + just deleted it. Restart ourselves */ + int i; + /* Ensure we have no open low fd's. */ + for (i = 3; i < 100; i++) { + close(i); + } + return execve(argv[0], argv, envp); + } + #if HAVE_SETPGID /* * If we're interactive we want to set our own process group for diff --git a/source/nsswitch/winbindd_cache.c b/source/nsswitch/winbindd_cache.c index 908d6ed19ae..4133611148c 100644 --- a/source/nsswitch/winbindd_cache.c +++ b/source/nsswitch/winbindd_cache.c @@ -855,7 +855,7 @@ static void wcache_save_lockout_policy(struct winbindd_domain *domain, NTSTATUS } 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); @@ -2594,6 +2594,267 @@ BOOL get_global_winbindd_state_offline(void) return global_winbindd_offline_state; } +/*********************************************************************** + Validate functions for all possible cache tdb keys. +***********************************************************************/ + +static int bad_cache_entry; + +static int validate_seqnum(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_ns(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_sn(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_u(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_loc_pol(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_pwd_pol(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_cred(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_ul(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_gl(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_ug(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_ua(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_gm(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_dr(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_de(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_trustdoms(TDB_DATA kbuf, TDB_DATA dbuf) +{ + return 0; +} + +static int validate_offline(TDB_DATA kbuf, TDB_DATA dbuf) +{ + 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)(TDB_DATA kbuf, TDB_DATA dbuf); +} key_val[] = { + {"SEQNUM/", validate_seqnum}, + {"NS/", validate_ns}, + {"SN/", validate_sn}, + {"U/", validate_u}, + {"LOC_POL/", validate_loc_pol}, + {"PWD_POL/", validate_pwd_pol}, + {"CRED/", validate_cred}, + {"UL/", validate_ul}, + {"GL/", validate_gl}, + {"UG/", validate_ug}, + {"UA", validate_ua}, + {"GM/", validate_gm}, + {"DR/", validate_dr}, + {"DE/", validate_de}, + {"TRUSTDOMS/", validate_trustdoms}, + {"WINBINDD_OFFLINE", validate_offline}, + {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; + + 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, kbuf.dptr, namelen)) == 0) { + return key_val[i].validate_data_fn(kbuf, dbuf); + } + } + + DEBUG(0,("cache_traverse_validate_fn: unknown cache entry\nkey :\n")); + dump_data(0, kbuf.dptr, kbuf.dsize); + DEBUG(0,("data :\n")); + dump_data(0, dbuf.dptr, dbuf.dsize); + return 1; /* terminate. */ +} + +/* 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); +} +#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, + delete the cache tdb and return non-zero - the caller (main winbindd + function) will restart us as we don't know if we crashed or not. +***********************************************************************/ + +int winbindd_validate_cache(void) +{ + BOOL ret = -1; + int fd = -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; + } + + tdb = tdb_open_log(cache_path, + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + lp_winbind_offline_logon() ? TDB_DEFAULT : (TDB_DEFAULT | TDB_CLEAR_IF_FIRST), + O_RDWR|O_CREAT, 0600); + if (!tdb) { + goto out; + } + + fd = 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", + cache_path)); + goto out; + } + + DEBUG(10,("winbindd_validate_cache: cache %s freelist has %d entries\n", + cache_path, num_entries)); + + /* 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", + cache_path)); + goto out; + } + + DEBUG(10,("winbindd_validate_cache: cache %s is good " + "with %d entries\n", cache_path, num_entries)); + ret = 0; /* Cache is good. */ + + out: + + /* 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 + + if (tdb) { + if (ret == 0) { + tdb_close(tdb); + } else if (fd != -1) { + close(fd); + } + } + + if (ret) { + unlink(cache_path); + } + + return ret; +} + /* the cache backend methods are exposed via this structure */ struct winbindd_methods cache_methods = { True, |