Re: Policy Plugins

Michael <[email protected]>
Newsgroups gmane.comp.tools.sudo.devel
Message-ID <VI1PR0801MB1278C19C03F91096FDFEF915EB770@VI1PR0801MB1278.eurprd08.prod.outlook.com>
Yup, MFA was a bad example.  For my project I need three policy plugins simultaneously:

1.  First one checks a signature in the target binary and if it is signed & valid allows the command to be run as root.  I might extend this to use certificates as the certificates can then carry constraints and allow further delegation of sudo management etc.
2.  Second one checks a remote enterprise repository for rules to see if the command is allowed.  It will keep a local expiring cache, but the remote repo could be offline.
3.  Last one is a break-glass local sudoers policy in case of network issues.

My first thought was to do the daisy chaining myself - so my policy plugin calls exported functions from another policy plugin and then eventually calls the original suoders policy plugin.  It could enable multiple policies without impacting the core.  I don't like the idea of a plugin that then provides child plugins and eventually (I suspect) my API would support extensions that aren't in the core and it all starts to diverge.  

Anyway, I've been playing with actually having multiple plugins enabled and have included an "alpha" patch (attached).  It isn't ready for a couple of reasons, but is good to have a look/play/check behaviors etc.  Notes:

a. Enables multiple policy plugins (implementation is the same as for io_plugins)
b. Implements a "first plugin to allow the command" strategy, but also allows plugins to return from check_policy() with a DENY which stops early.
c. Logical behavior for different MODEs (like VERSION, INVALIDATE, KILL, LIST etc) and changing some errors into warnings
d. Changed the sample_plugin to include "test" to approve command and "deny" to reject as valid passwords
e. Need to understand how debug_files is used and make sure the behavior is correct
f. Need to check how hooks are implemented and make sure the behavior is correct
g.  Need to test how MODE_EDIT behaves with multiple policies that support it

Another idea I've been asked about is the ability to go look up in a ticketing system if an approved incident or change ticket exists.  The ticket would have to be assigned to the invoker of sudo and be running on a server the ticket is raised against.  That is a cool idea and something that other people might benefit from - but, it is hardly something to include in the core of SUDO.  So, my initial response to that was I'll call a Python script and you can work it out from there.

I am also starting to think policy plugins will need to declare external requirements which might be met from another policy/authoriser plugin before a command is allowed (e.g. NEEDS_PASSWORD, NEEDS_TICKET, NEEDS_DIGITAL_SIGNARTURE).

Even if the new API splits out policy rules and approval then in my scenario I could have a sudoers rule allowing running a command as root without password but NEEDS_TICKET.  An approval plugin could then go away and check that.  If it fails then sudoers should carry on as I might also have a break-glass rule.  That would work.  But for a remote repository of sudo rules then this would not work.. :[

Big question for me is do I support multiple policy plugins by writing a current version compatible policy plugin that then supports multiple child policy plugins, or write it into the core of SUDO.  Happy to knock ideas around and try things out.


Mike

________________________________________
From: Todd C. Miller <[email protected]>
Sent: 13 May 2016 11:59
To: Michael
Cc: [email protected]
Subject: Re: [sudo-workers] Policy Plugins

The reason that only a single policy plugin is supported is that
combining multiple disparate security policies in a meaningful way
is hard to do.  How would you even analyze such a composite policy?

I don't think multi-factor authentication is a good reason to support
this, that is the job of things like PAM.

What I had been contemplating for a revamped plugin API is adding
approval plugins.  These would not grant privileges and could only
be used to deny a command that was allowed by the policy plugin.

Possible uses include:

1. Local time of day restrictions.

2. Explicit approval requirements from a more senior admin, potentially
   requiring the senior admin to sign-off on the command via XMPP
   or some other method.

 - todd

____________________________________________________________
sudo-workers mailing list <[email protected]>
For list information, options, or to unsubscribe, visit:
https://www.sudo.ws/mailman/listinfo/sudo-workers
multiple-policy-plugins.patch (application/octet-stream, 19.6 KB)
diff --git a/src/exec.c b/src/exec.c
index 1ceb99b..5611cc8 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -364,6 +364,16 @@ sudo_execute(struct command_details *details, struct command_status *cstat)
     }
 
     /*
+     * Check if any policy plugins have a close() function
+     */
+    int policy_close = false;
+    struct plugin_container *plugin;
+
+    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+        if (plugin->u.policy->close != NULL) policy_close = true;
+    }
+
+    /*
      * If we have an I/O plugin or the policy plugin has requested one, we
      * need to allocate a pty.  It is OK to set log_io in the pty-only case
      * as the io plugin tailqueue will be empty and no I/O logging will occur.
@@ -374,8 +384,7 @@ sudo_execute(struct command_details *details, struct command_status *cstat)
 	    utmp_user = details->utmp_user ? details->utmp_user : user_details.username;
 	sudo_debug_printf(SUDO_DEBUG_INFO, "allocate pty for I/O logging");
 	pty_setup(details->euid, user_details.tty, utmp_user);
-    } else if (!ISSET(details->flags, CD_SET_TIMEOUT|CD_SUDOEDIT) &&
-	policy_plugin.u.policy->close == NULL) {
+    } else if (!ISSET(details->flags, CD_SET_TIMEOUT|CD_SUDOEDIT) && !policy_close) {
 	/*
 	 * If there is no policy close function, no I/O logging or pty,
 	 * and we were not invoked as sudoedit, just exec directly.
diff --git a/src/load_plugins.c b/src/load_plugins.c
index 403ae03..8288941 100644
--- a/src/load_plugins.c
+++ b/src/load_plugins.c
@@ -150,7 +150,7 @@ sudo_check_plugin(struct plugin_info *info, char *fullpath, size_t pathsize)
  * Load the plugin specified by "info".
  */
 static bool
-sudo_load_plugin(struct plugin_container *policy_plugin,
+sudo_load_plugin(struct plugin_container_list *policy_plugins,
     struct plugin_container_list *io_plugins, struct plugin_info *info)
 {
     struct plugin_container *container = NULL;
@@ -196,30 +196,31 @@ sudo_load_plugin(struct plugin_container *policy_plugin,
 	goto bad;
     }
     if (plugin->type == SUDO_POLICY_PLUGIN) {
-	if (policy_plugin->handle != NULL) {
-	    /* Ignore duplicate entries. */
-	    if (strcmp(policy_plugin->name, info->symbol_name) != 0) {
-		sudo_warnx(U_("ignoring policy plugin `%s' in %s, line %d"),
+	/* Check for duplicate entries. */
+	TAILQ_FOREACH(container, policy_plugins, entries) {
+	    if (strcmp(container->name, info->symbol_name) == 0) {
+		sudo_warnx(U_("ignoring duplicate policy plugin `%s' in %s, line %d"),
 		    info->symbol_name, _PATH_SUDO_CONF, info->lineno);
-		sudo_warnx(U_("only a single policy plugin may be specified"));
-		goto bad;
+		sudo_dso_unload(handle);
+		handle = NULL;
+		break;
 	    }
-	    sudo_warnx(U_("ignoring duplicate policy plugin `%s' in %s, line %d"),
-		info->symbol_name, _PATH_SUDO_CONF, info->lineno);
-	    goto bad;
 	}
+        
 	if (handle != NULL) {
-	    policy_plugin->handle = handle;
-	    policy_plugin->path = strdup(path);
-	    if (policy_plugin->path == NULL) {
+	    container = calloc(1, sizeof(*container));
+	    if (container == NULL || (container->path = strdup(path)) == NULL) {
 		sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
 		goto bad;
 	    }
-	    policy_plugin->name = info->symbol_name;
-	    policy_plugin->options = info->options;
-	    policy_plugin->debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
-	    policy_plugin->u.generic = plugin;
-	    policy_plugin->debug_files = sudo_conf_debug_files(path);
+	    container->handle = handle;
+	    container->name = info->symbol_name;
+	    container->options = info->options;
+	    container->debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
+	    container->u.generic = plugin;
+	    // TODO: need to understand what "debug_files" is, and how it is used properly
+	    container->debug_files = sudo_conf_debug_files(path);
+	    TAILQ_INSERT_TAIL(policy_plugins, container, entries);
 	}
     } else if (plugin->type == SUDO_IO_PLUGIN) {
 	/* Check for duplicate entries. */
@@ -243,7 +244,8 @@ sudo_load_plugin(struct plugin_container *policy_plugin,
 	    container->options = info->options;
 	    container->debug_instance = SUDO_DEBUG_INSTANCE_INITIALIZER;
 	    container->u.generic = plugin;
-	    policy_plugin->debug_files = sudo_conf_debug_files(path);
+	    // TODO: need to understand what "debug_files" is, and how it is used properly
+	    container->debug_files = sudo_conf_debug_files(path);
 	    TAILQ_INSERT_TAIL(io_plugins, container, entries);
 	}
     }
@@ -273,7 +275,7 @@ free_plugin_info(struct plugin_info *info)
  * Load the plugins listed in sudo.conf.
  */
 bool
-sudo_load_plugins(struct plugin_container *policy_plugin,
+sudo_load_plugins(struct plugin_container_list *policy_plugins,
     struct plugin_container_list *io_plugins)
 {
     struct plugin_container *container;
@@ -285,7 +287,7 @@ sudo_load_plugins(struct plugin_container *policy_plugin,
     /* Walk the plugin list from sudo.conf, if any and free it. */
     plugins = sudo_conf_plugins();
     TAILQ_FOREACH_SAFE(info, plugins, entries, next) {
-	rval = sudo_load_plugin(policy_plugin, io_plugins, info);
+	rval = sudo_load_plugin(policy_plugins, io_plugins, info);
 	if (!rval)
 	    goto done;
 	free_plugin_info(info);
@@ -296,8 +298,9 @@ sudo_load_plugins(struct plugin_container *policy_plugin,
      * If no policy plugin, fall back to the default (sudoers).
      * If there is also no I/O log plugin, sudoers for that too.
      */
-    if (policy_plugin->handle == NULL) {
-	/* Default policy plugin */
+
+    /* Default policy plugin */
+    if (TAILQ_EMPTY(policy_plugins)) {
 	info = calloc(1, sizeof(*info));
 	if (info == NULL) {
 	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
@@ -306,39 +309,45 @@ sudo_load_plugins(struct plugin_container *policy_plugin,
 	info->symbol_name = "sudoers_policy";
 	info->path = SUDOERS_PLUGIN;
 	/* info->options = NULL; */
-	rval = sudo_load_plugin(policy_plugin, io_plugins, info);
+	rval = sudo_load_plugin(policy_plugins, io_plugins, info);
 	free(info);
 	if (!rval)
 	    goto done;
+    }
 
-	/* Default I/O plugin */
-	if (TAILQ_EMPTY(io_plugins)) {
-	    info = calloc(1, sizeof(*info));
-	    if (info == NULL) {
-		sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
-		goto done;
-	    }
-	    info->symbol_name = "sudoers_io";
-	    info->path = SUDOERS_PLUGIN;
-	    /* info->options = NULL; */
-	    rval = sudo_load_plugin(policy_plugin, io_plugins, info);
-	    free(info);
-	    if (!rval)
-		goto done;
+    /* Default I/O plugin */
+    if (TAILQ_EMPTY(io_plugins)) {
+        info = calloc(1, sizeof(*info));
+	if (info == NULL) {
+	    sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+	    goto done;
 	}
+	info->symbol_name = "sudoers_io";
+	info->path = SUDOERS_PLUGIN;
+	/* info->options = NULL; */
+	rval = sudo_load_plugin(policy_plugins, io_plugins, info);
+	free(info);
+	if (!rval)
+	    goto done;
     }
-    if (policy_plugin->u.policy->check_policy == NULL) {
-	sudo_warnx(U_("policy plugin %s does not include a check_policy method"),
-	    policy_plugin->name);
-	rval = false;
-	goto done;
+
+    TAILQ_FOREACH(container, policy_plugins, entries) {
+        if (container->u.policy->check_policy == NULL) {
+	    sudo_warnx(U_("policy plugin %s does not include a check_policy method"),
+	        container->name);
+	    rval = false;
+	    goto done;
+        }
     }
 
+    // TODO: investigate this to see if installing hooks across multiple policy plugins works correctly
     /* Install hooks (XXX - later). */
     sudo_debug_set_active_instance(SUDO_DEBUG_INSTANCE_INITIALIZER);
-    if (policy_plugin->u.policy->version >= SUDO_API_MKVERSION(1, 2)) {
-	if (policy_plugin->u.policy->register_hooks != NULL)
-	    policy_plugin->u.policy->register_hooks(SUDO_HOOK_VERSION, register_hook);
+    TAILQ_FOREACH(container, policy_plugins, entries) {
+        if (container->u.policy->version >= SUDO_API_MKVERSION(1, 2)) {
+	    if (container->u.policy->register_hooks != NULL)
+	        container->u.policy->register_hooks(SUDO_HOOK_VERSION, register_hook);
+        }
     }
     TAILQ_FOREACH(container, io_plugins, entries) {
 	if (container->u.io->version >= SUDO_API_MKVERSION(1, 2)) {
diff --git a/src/sudo.c b/src/sudo.c
index d4be82a..ad2622e 100644
--- a/src/sudo.c
+++ b/src/sudo.c
@@ -79,7 +79,7 @@
 /*
  * Local variables
  */
-struct plugin_container policy_plugin;
+struct plugin_container_list policy_plugins = TAILQ_HEAD_INITIALIZER(policy_plugins);
 struct plugin_container_list io_plugins = TAILQ_HEAD_INITIALIZER(io_plugins);
 struct user_details user_details;
 const char *list_user; /* extern for parse_args.c */
@@ -218,23 +218,27 @@ main(int argc, char *argv[], char *envp[])
     sudo_warn_set_conversation(sudo_conversation);
 
     /* Load plugins. */
-    if (!sudo_load_plugins(&policy_plugin, &io_plugins))
+    if (!sudo_load_plugins(&policy_plugins, &io_plugins))
 	sudo_fatalx(U_("fatal error, unable to load plugins"));
 
     /* Open policy plugin. */
-    ok = policy_open(&policy_plugin, settings, user_info, envp);
-    if (ok != 1) {
-	if (ok == -2)
-	    usage(1);
-	else
-	    sudo_fatalx(U_("unable to initialize policy plugin"));
+    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	ok = policy_open(plugin, settings, user_info, envp);
+	if (ok != 1) {
+	    if (ok == -2)
+		usage(1);
+	    else
+		sudo_fatalx(U_("unable to initialize policy plugin"));
+	}
     }
 
     init_signals();
 
     switch (sudo_mode & MODE_MASK) {
 	case MODE_VERSION:
-	    policy_show_version(&policy_plugin, !user_details.uid);
+	    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+		policy_show_version(plugin, !user_details.uid);
+	    }
 	    TAILQ_FOREACH(plugin, &io_plugins, entries) {
 		ok = iolog_open(plugin, settings, user_info, NULL,
 		    nargc, nargv, envp);
@@ -244,76 +248,95 @@ main(int argc, char *argv[], char *envp[])
 	    break;
 	case MODE_VALIDATE:
 	case MODE_VALIDATE|MODE_INVALIDATE:
-	    ok = policy_validate(&policy_plugin);
-	    exit(ok != 1);
+            ok=0;
+            TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	        if (policy_validate(plugin) != 1) {
+                    ok=1;
+                }
+            }
+            exit(ok);
+	    break;
 	case MODE_KILL:
 	case MODE_INVALIDATE:
-	    policy_invalidate(&policy_plugin, sudo_mode == MODE_KILL);
+	    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	        policy_invalidate(plugin, sudo_mode == MODE_KILL);
+            }
 	    exit(0);
 	    break;
 	case MODE_CHECK:
 	case MODE_CHECK|MODE_INVALIDATE:
 	case MODE_LIST:
 	case MODE_LIST|MODE_INVALIDATE:
-	    ok = policy_list(&policy_plugin, nargc, nargv,
-		ISSET(sudo_mode, MODE_LONG_LIST), list_user);
-	    exit(ok != 1);
+            ok=0;
+	    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	        if (policy_list(plugin, nargc, nargv,
+		    ISSET(sudo_mode, MODE_LONG_LIST), list_user) != 1) {
+                    ok=1;
+                }
+            }
+	    exit(ok);
 	case MODE_EDIT:
 	case MODE_RUN:
-	    ok = policy_check(&policy_plugin, nargc, nargv, env_add,
-		&command_info, &argv_out, &user_env_out);
-	    sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
-	    if (ok != 1) {
-		if (ok == -2)
-		    usage(1);
-		exit(1); /* plugin printed error message */
-	    }
-	    /* Reset nargv/nargc based on argv_out. */
-	    /* XXX - leaks old nargv in shell mode */
-	    for (nargv = argv_out, nargc = 0; nargv[nargc] != NULL; nargc++)
-		continue;
-	    if (nargc == 0)
-		sudo_fatalx(U_("plugin did not return a command to execute"));
-	    /* Open I/O plugins once policy plugin succeeds. */
-	    TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
-		ok = iolog_open(plugin, settings, user_info,
-		    command_info, nargc, nargv, user_env_out);
-		switch (ok) {
-		case 1:
-		    break;
-		case 0:
-		    /* I/O plugin asked to be disabled, remove and free. */
-		    iolog_unlink(plugin);
-		    break;
-		case -2:
-		    usage(1);
-		    break;
-		default:
-		    sudo_fatalx(U_("error initializing I/O plugin %s"),
-			plugin->name);
-		}
-	    }
-	    /* Setup command details and run command/edit. */
-	    command_info_to_details(command_info, &command_details);
-	    command_details.argv = argv_out;
-	    command_details.envp = user_env_out;
-	    if (ISSET(sudo_mode, MODE_BACKGROUND))
-		SET(command_details.flags, CD_BACKGROUND);
-	    /* Become full root (not just setuid) so user cannot kill us. */
-	    if (setuid(ROOT_UID) == -1)
-		sudo_warn("setuid(%d)", ROOT_UID);
-	    /* Restore coredumpsize resource limit before running. */
+	    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	        ok = policy_check(plugin, nargc, nargv, env_add,
+		    &command_info, &argv_out, &user_env_out);
+	        sudo_debug_printf(SUDO_DEBUG_INFO, "policy plugin returns %d", ok);
+		if (ok < 0) {
+                    if (ok == -2)
+		        usage(1);
+                    exit(1); /* plugin printed error message */
+                }
+                if (ok == 1) {
+	            /* Reset nargv/nargc based on argv_out. */
+	            /* XXX - leaks old nargv in shell mode */
+	            for (nargv = argv_out, nargc = 0; nargv[nargc] != NULL; nargc++)
+		        continue;
+	            if (nargc == 0)
+		        sudo_fatalx(U_("plugin did not return a command to execute"));
+
+	            /* Open I/O plugins once policy plugin succeeds. */
+	            TAILQ_FOREACH_SAFE(plugin, &io_plugins, entries, next) {
+		        ok = iolog_open(plugin, settings, user_info,
+		            command_info, nargc, nargv, user_env_out);
+		        switch (ok) {
+		        case 1:
+		            break;
+		        case 0:
+		            /* I/O plugin asked to be disabled, remove and free. */
+		            iolog_unlink(plugin);
+		            break;
+		        case -2:
+		            usage(1);
+		            break;
+		        default:
+		            sudo_fatalx(U_("error initializing I/O plugin %s"),
+			        plugin->name);
+		        }
+	            }
+	            /* Setup command details and run command/edit. */
+	            command_info_to_details(command_info, &command_details);
+	            command_details.argv = argv_out;
+	            command_details.envp = user_env_out;
+	            if (ISSET(sudo_mode, MODE_BACKGROUND))
+		        SET(command_details.flags, CD_BACKGROUND);
+	            /* Become full root (not just setuid) so user cannot kill us. */
+	            if (setuid(ROOT_UID) == -1)
+		        sudo_warn("setuid(%d)", ROOT_UID);
+	            /* Restore coredumpsize resource limit before running. */
 #ifdef RLIMIT_CORE
-	    if (sudo_conf_disable_coredump())
-		(void) setrlimit(RLIMIT_CORE, &corelimit);
+	            if (sudo_conf_disable_coredump())
+		        (void) setrlimit(RLIMIT_CORE, &corelimit);
 #endif /* RLIMIT_CORE */
-	    if (ISSET(command_details.flags, CD_SUDOEDIT)) {
-		status = sudo_edit(&command_details);
-	    } else {
-		status = run_command(&command_details);
-	    }
-	    /* The close method was called by sudo_edit/run_command. */
-	    break;
+	            if (ISSET(command_details.flags, CD_SUDOEDIT)) {
+		        status = sudo_edit(&command_details);
+	            } else {
+		        status = run_command(&command_details);
+	            }
+	            /* The close method was called by sudo_edit/run_command. */
+                    break;
+                }
+            }
+            break;
 	default:
 	    sudo_fatalx(U_("unexpected sudo mode 0x%x"), sudo_mode);
     }
@@ -1128,7 +1151,9 @@ run_command(struct command_details *details)
 	/* exec_setup() or execve() returned an error. */
 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
 	    "calling policy close with errno %d", cstat.val);
-	policy_close(&policy_plugin, 0, cstat.val);
+	TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	    policy_close(plugin, 0, cstat.val);
+        }
 	TAILQ_FOREACH(plugin, &io_plugins, entries) {
 	    sudo_debug_printf(SUDO_DEBUG_DEBUG,
 		"calling I/O close with errno %d", cstat.val);
@@ -1145,7 +1170,9 @@ run_command(struct command_details *details)
 #endif
 	sudo_debug_printf(SUDO_DEBUG_DEBUG,
 	    "calling policy close with wait status %d", status);
-	policy_close(&policy_plugin, status, 0);
+	TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+	    policy_close(plugin, status, 0);
+        }
 	TAILQ_FOREACH(plugin, &io_plugins, entries) {
 	    sudo_debug_printf(SUDO_DEBUG_DEBUG,
 		"calling I/O close with wait status %d", status);
@@ -1349,12 +1376,13 @@ policy_invalidate(struct plugin_container *plugin, int remove)
 {
     debug_decl(policy_invalidate, SUDO_DEBUG_PCOMM)
     if (plugin->u.policy->invalidate == NULL) {
-	sudo_fatalx(U_("policy plugin %s does not support the -k/-K options"),
+	sudo_warnx(U_("policy plugin %s does not support the -k/-K options"),
 	    plugin->name);
+    } else {
+        sudo_debug_set_active_instance(plugin->debug_instance);
+        plugin->u.policy->invalidate(remove);
+        sudo_debug_set_active_instance(sudo_debug_instance);
     }
-    sudo_debug_set_active_instance(plugin->debug_instance);
-    plugin->u.policy->invalidate(remove);
-    sudo_debug_set_active_instance(sudo_debug_instance);
     debug_return;
 }
 
@@ -1362,23 +1390,30 @@ int
 policy_init_session(struct command_details *details)
 {
     int rval = true;
+    struct plugin_container *plugin;
     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
 
-    if (policy_plugin.u.policy->init_session) {
-	/*
-	 * Backwards compatibility for older API versions
-	 */
-	sudo_debug_set_active_instance(policy_plugin.debug_instance);
-	switch (policy_plugin.u.generic->version) {
-	case SUDO_API_MKVERSION(1, 0):
-	case SUDO_API_MKVERSION(1, 1):
-	    rval = policy_plugin.u.policy_1_0->init_session(details->pw);
-	    break;
-	default:
-	    rval = policy_plugin.u.policy->init_session(details->pw,
-		&details->envp);
-	}
-	sudo_debug_set_active_instance(sudo_debug_instance);
+    TAILQ_FOREACH(plugin, &policy_plugins, entries) {
+        if (plugin->u.policy->init_session) {
+	    /*
+	     * Backwards compatibility for older API versions
+	     */
+	    sudo_debug_set_active_instance(plugin->debug_instance);
+	    switch (plugin->u.generic->version) {
+	    case SUDO_API_MKVERSION(1, 0):
+	    case SUDO_API_MKVERSION(1, 1):
+	        if (!plugin->u.policy_1_0->init_session(details->pw)) {
+                    rval=false;
+                }
+	        break;
+	    default:
+	        if (plugin->u.policy->init_session(details->pw,
+		    &details->envp)) {
+                    rval = false;
+                }
+	    }
+	    sudo_debug_set_active_instance(sudo_debug_instance);
+        }
     }
     debug_return_int(rval);
 }
@@ -1541,7 +1576,11 @@ gc_run(void)
     }
 
     /* Free plugin structs. */
-    free(policy_plugin.path);
+    while ((plugin = TAILQ_FIRST(&policy_plugins))) {
+	TAILQ_REMOVE(&policy_plugins, plugin, entries);
+	free(plugin->path);
+	free(plugin);
+    }
     while ((plugin = TAILQ_FIRST(&io_plugins))) {
 	TAILQ_REMOVE(&io_plugins, plugin, entries);
 	free(plugin->path);
diff --git a/src/sudo_plugin_int.h b/src/sudo_plugin_int.h
index 7f23712..32d3381 100644
--- a/src/sudo_plugin_int.h
+++ b/src/sudo_plugin_int.h
@@ -102,7 +102,7 @@ struct plugin_container {
 };
 TAILQ_HEAD(plugin_container_list, plugin_container);
 
-extern struct plugin_container policy_plugin;
+extern struct plugin_container_list policy_plugins;
 extern struct plugin_container_list io_plugins;
 
 int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
@@ -111,7 +111,7 @@ int sudo_conversation_1_7(int num_msgs, const struct sudo_conv_message msgs[],
     struct sudo_conv_reply replies[]);
 int sudo_conversation_printf(int msg_type, const char *fmt, ...);
 
-bool sudo_load_plugins(struct plugin_container *policy_plugin,
+bool sudo_load_plugins(struct plugin_container_list *policy_plugins,
     struct plugin_container_list *io_plugins);
 
 #endif /* SUDO_PLUGIN_INT_H */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.