r19957: Initial framework to make winbindd robust
authorJeremy Allison <jra@samba.org>
Wed, 29 Nov 2006 23:20:22 +0000 (23:20 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:16:15 +0000 (12:16 -0500)
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/lib/replace/system/wait.h
source3/nsswitch/wbinfo.c
source3/nsswitch/winbindd.c
source3/nsswitch/winbindd_cache.c

index c2041a5938cab16f0206aec9c66e3bd74af00440..47e3e84bb119e55e8aebf083924e89720804fcae 100644 (file)
@@ -36,4 +36,6 @@
 #define SIGNAL_CAST (RETSIGTYPE (*)(int))
 #endif
 
+#include <setjmp.h>
+
 #endif
index ffca4121fac4ed8b4e2bb45ec512e7bf29d0cad9..cabf995042a237eee04cc772fbc09d669e6e91a3 100644 (file)
@@ -1182,7 +1182,7 @@ enum {
        OPT_GROUP_INFO,
 };
 
-int main(int argc, char **argv)
+int main(int argc, char **argv, char **envp)
 {
        int opt;
 
index e9e51449d656fe609909ff7537eef3137c32baac..41662900ce725d2d277bf661a4432bf6c03fc91f 100644 (file)
@@ -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);
index baaa5826bbd12066b6c12adfe45b98290b78ea07..e9b0d2e8f9a68c7a9b17796f68c29ed394d83339 100644 (file)
@@ -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,