From ed34ffb14792cc47ca1af5ffeb076f3c2d7f28dc Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 29 Nov 2006 23:20:22 +0000 Subject: r19957: Initial framework to make winbindd robust against tdb corruption. Needs fleshing out (and I forgot one record type) and needs helpful suggestion from Volker to validate freelist, but should give an idea of how this will look. Jeremy. (This used to be commit 8eb53f74e414483afde7b1e38ea2a3f56ae3ec66) --- source3/nsswitch/wbinfo.c | 2 +- source3/nsswitch/winbindd.c | 16 ++- source3/nsswitch/winbindd_cache.c | 230 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+), 2 deletions(-) (limited to 'source3/nsswitch') diff --git a/source3/nsswitch/wbinfo.c b/source3/nsswitch/wbinfo.c index ffca4121fa..cabf995042 100644 --- a/source3/nsswitch/wbinfo.c +++ b/source3/nsswitch/wbinfo.c @@ -1182,7 +1182,7 @@ enum { OPT_GROUP_INFO, }; -int main(int argc, char **argv) +int main(int argc, char **argv, char **envp) { int opt; diff --git a/source3/nsswitch/winbindd.c b/source3/nsswitch/winbindd.c index e9e51449d6..41662900ce 100644 --- a/source3/nsswitch/winbindd.c +++ b/source3/nsswitch/winbindd.c @@ -879,7 +879,7 @@ static void process_loop(void) struct winbindd_state server_state; /* Server state information */ -int main(int argc, char **argv) +int main(int argc, char **argv, char **envp) { pstring logfile; static BOOL Fork = True; @@ -1022,6 +1022,17 @@ int main(int argc, char **argv) pidfile_create("winbindd"); + 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 @@ -1040,6 +1051,9 @@ int main(int argc, char **argv) exit(1); } + /* Ensure all cache and idmap caches are consistent + before we startup. */ + /* React on 'smbcontrol winbindd reload-config' in the same way as to SIGHUP signal */ message_register(MSG_SMB_CONF_UPDATED, msg_reload_services); diff --git a/source3/nsswitch/winbindd_cache.c b/source3/nsswitch/winbindd_cache.c index baaa5826bb..e9b0d2e8f9 100644 --- a/source3/nsswitch/winbindd_cache.c +++ b/source3/nsswitch/winbindd_cache.c @@ -2594,6 +2594,236 @@ BOOL get_global_winbindd_state_offline(void) return global_winbindd_offline_state; } +/*********************************************************************** + Validate functions for all possible cache tdb keys. +***********************************************************************/ + +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_trustdoms(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}, + {"TRUSTDOMS/", validate_trustdoms}, + {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; + + /* Ensure key is valid. */ + if (kbuf.dsize < 3) { + return 1; /* terminate. */ + } + /* Ensure key is a string. */ + if (kbuf.dptr[kbuf.dsize] != '\0') { + return 1; /* terminate. */ + } + + for (i = 0; key_val[i].keyname; i++) { + if (strncmp(key_val[i].keyname, kbuf.dptr, strlen(key_val[i].keyname)) == 0) { + if (key_val[i].validate_data_fn(kbuf, dbuf)) { + return 1; /* terminate. */ + } + } + } + return 0; +} + +/* 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; + 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); + + /* Now traverse the cache to validate it. */ + if (tdb_traverse(tdb, cache_traverse_validate_fn, NULL)) { + goto out; + } + + DEBUG(10,("winbindd_validate_cache: cache %s is good\n", cache_path)); + 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, -- cgit