]> granicus.if.org Git - sudo/commitdiff
Add support for deregistering hooks. If an I/O log plugin fails
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 8 Mar 2012 16:29:32 +0000 (11:29 -0500)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 8 Mar 2012 16:29:32 +0000 (11:29 -0500)
to initialize, deregister its hooks (if any).

src/hooks.c
src/sudo.c

index e44bfcb23a7b7ed96c1fcf361c18fa6e2e88ae86..5e8a395edc6ec2be0321ab4d7227f77f2b395cdf 100644 (file)
 #include "sudo_plugin_int.h"
 #include "sudo_debug.h"
 
-/* XXX - autogen from config file? */
-/* XXX - implement deregister_hook */
-
-/* HOOK: setenv */
-
-static struct sudo_hook_setenv {
-    struct sudo_hook_setenv *next;
-    sudo_hook_fn_setenv_t hook_fn;
+/* Singly linked hook list. */
+struct sudo_hook_list {
+    struct sudo_hook_list *next;
+    union {
+       sudo_hook_fn_t generic_fn;
+       sudo_hook_fn_setenv_t setenv_fn;
+       sudo_hook_fn_unsetenv_t unsetenv_fn;
+       sudo_hook_fn_getenv_t getenv_fn;
+       sudo_hook_fn_putenv_t putenv_fn;
+    } u;
     void *closure;
-} *sudo_hook_setenv_list;
-
-static void
-register_hook_setenv(int (*hook_fn)(), void *closure)
-{
-    struct sudo_hook_setenv *hook;
-    debug_decl(add_hook_setenv, SUDO_DEBUG_HOOKS)
-
-    hook = emalloc(sizeof(*hook));
-    hook->hook_fn = (sudo_hook_fn_setenv_t)hook_fn;
-    hook->closure = closure;
-    hook->next = sudo_hook_setenv_list;
-    sudo_hook_setenv_list = hook;
+};
 
-    debug_return;
-}
+/* Each hook type gets own hook list. */
+static struct sudo_hook_list *sudo_hook_setenv_list;
+static struct sudo_hook_list *sudo_hook_unsetenv_list;
+static struct sudo_hook_list *sudo_hook_getenv_list;
+static struct sudo_hook_list *sudo_hook_putenv_list;
 
 int
 process_hooks_setenv(const char *name, const char *value, int overwrite)
 {
-    struct sudo_hook_setenv *hook;
+    struct sudo_hook_list *hook;
     int rc = SUDO_HOOK_RET_NEXT;
     debug_decl(process_hooks_setenv, SUDO_DEBUG_HOOKS)
 
     /* First process the hooks. */
     for (hook = sudo_hook_setenv_list; hook != NULL; hook = hook->next) {
-       rc = hook->hook_fn(name, value, overwrite, hook->closure);
+       rc = hook->u.setenv_fn(name, value, overwrite, hook->closure);
        switch (rc) {
            case SUDO_HOOK_RET_NEXT:
                break;
@@ -92,39 +85,16 @@ done:
     debug_return_int(rc);
 }
 
-/* HOOK: putenv */
-
-static struct sudo_hook_putenv {
-    struct sudo_hook_putenv *next;
-    sudo_hook_fn_putenv_t hook_fn;
-    void *closure;
-} *sudo_hook_putenv_list;
-
-static void
-register_hook_putenv(int (*hook_fn)(), void *closure)
-{
-    struct sudo_hook_putenv *hook;
-    debug_decl(add_hook_putenv, SUDO_DEBUG_HOOKS)
-
-    hook = emalloc(sizeof(*hook));
-    hook->hook_fn = (sudo_hook_fn_putenv_t)hook_fn;
-    hook->closure = closure;
-    hook->next = sudo_hook_putenv_list;
-    sudo_hook_putenv_list = hook;
-
-    debug_return;
-}
-
 int
 process_hooks_putenv(char *string)
 {
-    struct sudo_hook_putenv *hook;
+    struct sudo_hook_list *hook;
     int rc = SUDO_HOOK_RET_NEXT;
     debug_decl(process_hooks_putenv, SUDO_DEBUG_HOOKS)
 
     /* First process the hooks. */
     for (hook = sudo_hook_putenv_list; hook != NULL; hook = hook->next) {
-       rc = hook->hook_fn(string, hook->closure);
+       rc = hook->u.putenv_fn(string, hook->closure);
        switch (rc) {
            case SUDO_HOOK_RET_NEXT:
                break;
@@ -140,40 +110,17 @@ done:
     debug_return_int(rc);
 }
 
-/* HOOK: getenv */
-
-static struct sudo_hook_getenv {
-    struct sudo_hook_getenv *next;
-    sudo_hook_fn_getenv_t hook_fn;
-    void *closure;
-} *sudo_hook_getenv_list;
-
-static void
-register_hook_getenv(int (*hook_fn)(), void *closure)
-{
-    struct sudo_hook_getenv *hook;
-    debug_decl(add_hook_putenv, SUDO_DEBUG_HOOKS)
-
-    hook = emalloc(sizeof(*hook));
-    hook->hook_fn = (sudo_hook_fn_getenv_t)hook_fn;
-    hook->closure = closure;
-    hook->next = sudo_hook_getenv_list;
-    sudo_hook_getenv_list = hook;
-
-    debug_return;
-}
-
 int
 process_hooks_getenv(const char *name, char **value)
 {
-    struct sudo_hook_getenv *hook;
+    struct sudo_hook_list *hook;
     char *val = NULL;
     int rc = SUDO_HOOK_RET_NEXT;
     debug_decl(process_hooks_getenv, SUDO_DEBUG_HOOKS)
 
     /* First process the hooks. */
     for (hook = sudo_hook_getenv_list; hook != NULL; hook = hook->next) {
-       rc = hook->hook_fn(name, &val, hook->closure);
+       rc = hook->u.getenv_fn(name, &val, hook->closure);
        switch (rc) {
            case SUDO_HOOK_RET_NEXT:
                break;
@@ -191,39 +138,16 @@ done:
     debug_return_int(rc);
 }
 
-/* HOOK: unsetenv */
-
-static struct sudo_hook_unsetenv {
-    struct sudo_hook_unsetenv *next;
-    sudo_hook_fn_unsetenv_t hook_fn;
-    void *closure;
-} *sudo_hook_unsetenv_list;
-
-static void
-register_hook_unsetenv(int (*hook_fn)(), void *closure)
-{
-    struct sudo_hook_unsetenv *hook;
-    debug_decl(add_hook_unsetenv, SUDO_DEBUG_HOOKS)
-
-    hook = emalloc(sizeof(*hook));
-    hook->hook_fn = (sudo_hook_fn_unsetenv_t)hook_fn;
-    hook->closure = closure;
-    hook->next = sudo_hook_unsetenv_list;
-    sudo_hook_unsetenv_list = hook;
-
-    debug_return;
-}
-
 int
 process_hooks_unsetenv(const char *name)
 {
-    struct sudo_hook_unsetenv *hook;
+    struct sudo_hook_list *hook;
     int rc = SUDO_HOOK_RET_NEXT;
     debug_decl(process_hooks_unsetenv, SUDO_DEBUG_HOOKS)
 
     /* First process the hooks. */
     for (hook = sudo_hook_unsetenv_list; hook != NULL; hook = hook->next) {
-       rc = hook->hook_fn(name, hook->closure);
+       rc = hook->u.unsetenv_fn(name, hook->closure);
        switch (rc) {
            case SUDO_HOOK_RET_NEXT:
                break;
@@ -239,6 +163,23 @@ done:
     debug_return_int(rc);
 }
 
+/* Hook registration internals. */
+static void
+register_hook_internal(struct sudo_hook_list **head,
+    int (*hook_fn)(), void *closure)
+{
+    struct sudo_hook_list *hook;
+    debug_decl(register_hook_internal, SUDO_DEBUG_HOOKS)
+
+    hook = emalloc(sizeof(*hook));
+    hook->u.generic_fn = hook_fn;
+    hook->closure = closure;
+    hook->next = *head;
+    *head = hook;
+
+    debug_return;
+}
+
 /* Register the specified hook. */
 int
 register_hook(struct sudo_hook *hook)
@@ -252,16 +193,81 @@ register_hook(struct sudo_hook *hook)
     } else {
        switch (hook->hook_type) {
            case SUDO_HOOK_GETENV:
-               register_hook_getenv(hook->hook_fn, hook->closure);
+               register_hook_internal(&sudo_hook_getenv_list, hook->hook_fn,
+                   hook->closure);
+               break;
+           case SUDO_HOOK_PUTENV:
+               register_hook_internal(&sudo_hook_putenv_list, hook->hook_fn,
+                   hook->closure);
+               break;
+           case SUDO_HOOK_SETENV:
+               register_hook_internal(&sudo_hook_setenv_list, hook->hook_fn,
+                   hook->closure);
+               break;
+           case SUDO_HOOK_UNSETENV:
+               register_hook_internal(&sudo_hook_unsetenv_list, hook->hook_fn,
+                   hook->closure);
+               break;
+           default:
+               /* XXX - use define for unknown value */
+               rval = 1;
+               break;
+       }
+    }
+
+    debug_return_int(rval);
+}
+
+/* Hook deregistration internals. */
+static void
+deregister_hook_internal(struct sudo_hook_list **head,
+    int (*hook_fn)(), void *closure)
+{
+    struct sudo_hook_list *hook, *prev = NULL;
+    debug_decl(deregister_hook_internal, SUDO_DEBUG_HOOKS)
+
+    for (hook = *head, prev = NULL; hook != NULL; prev = hook, hook = hook->next) {
+       if (hook->u.generic_fn == hook_fn && hook->closure == closure) {
+           /* Remove from list and free. */
+           if (prev == NULL)
+               *head = hook->next;
+           else
+               prev->next = hook->next;
+           efree(hook);
+           break;
+       }
+    }
+
+    debug_return;
+}
+
+/* Deregister the specified hook. */
+int
+deregister_hook(struct sudo_hook *hook)
+{
+    int rval = 0;
+    debug_decl(deregister_hook, SUDO_DEBUG_HOOKS)
+
+    if (SUDO_HOOK_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
+       /* Major versions must match. */
+       rval = -1;
+    } else {
+       switch (hook->hook_type) {
+           case SUDO_HOOK_GETENV:
+               deregister_hook_internal(&sudo_hook_getenv_list, hook->hook_fn,
+                   hook->closure);
                break;
            case SUDO_HOOK_PUTENV:
-               register_hook_putenv(hook->hook_fn, hook->closure);
+               deregister_hook_internal(&sudo_hook_putenv_list, hook->hook_fn,
+                   hook->closure);
                break;
            case SUDO_HOOK_SETENV:
-               register_hook_setenv(hook->hook_fn, hook->closure);
+               deregister_hook_internal(&sudo_hook_setenv_list, hook->hook_fn,
+                   hook->closure);
                break;
            case SUDO_HOOK_UNSETENV:
-               register_hook_unsetenv(hook->hook_fn, hook->closure);
+               deregister_hook_internal(&sudo_hook_unsetenv_list, hook->hook_fn,
+                   hook->closure);
                break;
            default:
                /* XXX - use define for unknown value */
index 3bc99ada483931c08e1ba86eb7d1271dfc27b170..00cb7238b665f77c1425659d255b6641a77d4a02 100644 (file)
@@ -139,6 +139,7 @@ static int iolog_open(struct plugin_container *plugin, char * const settings[],
 static void iolog_close(struct plugin_container *plugin, int exit_status,
     int error);
 static int iolog_show_version(struct plugin_container *plugin, int verbose);
+static void iolog_unlink(struct plugin_container *plugin);
 
 #ifdef RLIMIT_CORE
 static struct rlimit corelimit;
@@ -275,8 +276,8 @@ main(int argc, char *argv[], char *envp[])
                case 1:
                    break;
                case 0:
-                   /* I/O plugin asked to be disabled, remove from list. */
-                   tq_remove(&io_plugins, plugin);
+                   /* I/O plugin asked to be disabled, remove and free. */
+                   iolog_unlink(plugin);
                    break;
                case -2:
                    usage(1);
@@ -1197,3 +1198,25 @@ iolog_show_version(struct plugin_container *plugin, int verbose)
     debug_decl(iolog_show_version, SUDO_DEBUG_PCOMM)
     debug_return_bool(plugin->u.io->show_version(verbose));
 }
+
+/*
+ * Remove the specified I/O logging plugin from the io_plugins list.
+ * Deregisters any hooks before unlinking, then frees the container.
+ */
+static void
+iolog_unlink(struct plugin_container *plugin)
+{
+    debug_decl(iolog_unlink, SUDO_DEBUG_PCOMM)
+
+    /* Deregister hooks, if any. */
+    if (plugin->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
+       if (plugin->u.io->deregister_hooks != NULL)
+           plugin->u.io->deregister_hooks(SUDO_HOOK_VERSION,
+               deregister_hook);
+    }
+    /* Remove from io_plugins list and free. */
+    tq_remove(&io_plugins, plugin);
+    efree(plugin);
+
+    debug_return;
+}