]> granicus.if.org Git - sudo/commitdiff
Pass a pointer to user_env in to the init_session policy plugin
authorTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 15 Mar 2012 13:18:36 +0000 (09:18 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Thu, 15 Mar 2012 13:18:36 +0000 (09:18 -0400)
function so session setup can modify the user environment as needed.
For PAM authentication, merge the PAM environment with the user
environment at init_session time.  We no longer need to swap in the
user_env for environ during session init, nor do we need to disable
the env hooks at init_session time.

doc/sudo_plugin.pod
include/sudo_plugin.h
plugins/sudoers/auth/pam.c
plugins/sudoers/auth/sudo_auth.c
plugins/sudoers/auth/sudo_auth.h
plugins/sudoers/env.c
plugins/sudoers/sudoers.c
plugins/sudoers/sudoers.h
src/hooks.c
src/sudo.c
src/sudo.h

index 5868d67a6e5236752b8f172d787bf01e23478b80..c9d831e75871567abdac8c51ac51ad34808215cc 100644 (file)
@@ -102,7 +102,7 @@ so that B<sudo> can load it.
                  const char *list_user);
      int (*validate)(void);
      void (*invalidate)(int remove);
-     int (*init_session)(struct passwd *pwd);
+     int (*init_session)(struct passwd *pwd, char **user_env[]);
      void (*register_hooks)(int version,
        int (*register_hook)(struct sudo_hook *hook));
      void (*deregister_hooks)(int version,
@@ -384,7 +384,7 @@ itself but the I<value> might.
 
 Any (non-comment) strings immediately after the plugin path are
 treated as arguments to the plugin.  These arguments are split on
-a whitespace boundary and are passed to the plugin in the form of
+a white space boundary and are passed to the plugin in the form of
 a C<NULL>-terminated array of strings.  If no arguments were
 specified, I<plugin_args> will be the NULL pointer.
 
@@ -742,7 +742,7 @@ support credential caching.
 
 =item init_session
 
- int (*init_session)(struct passwd *pwd);
+ int (*init_session)(struct passwd *pwd, char **user_envp[);
 
 The C<init_session> function is called when B<sudo> sets up the
 execution environment for the command, immediately before the
@@ -754,6 +754,18 @@ The I<pwd> argument points to a passwd struct for the user the
 command will be run as if the uid the command will run as was found
 in the password database, otherwise it will be NULL.
 
+The I<user_env> argument points to the environment the command will
+run in, in the form of a C<NULL>-terminated vector of "name=value"
+strings.  This is the same string passed back to the front end via
+the Policy Plugin's I<user_env_out> parameter.  If the C<init_session>
+function needs to modify the user environment, it should update the
+pointer stored in I<user_env>.  The expected use case is to merge
+the contents of the PAM environment (if any) with the contents of
+I<user_env>.  NOTE: the I<user_env> parameter is only available
+starting with API version 1.2.  A plugin B<must> check the API
+version specified by the B<sudo> front end before using I<user_env>.
+Failure to do so may result in a crash.
+
 Returns 1 on success, 0 on failure and -1 on error.
 On error, the plugin may optionally call the conversation or plugin_printf
 function with C<SUDO_CONF_ERROR_MSG> to present additional
@@ -771,7 +783,7 @@ hooks, C<register_hooks> should be set to the NULL pointer.
 The I<version> argument describes the version of the hooks API
 supported by the B<sudo> front end.
 
-The C<register_hook> function should be used to register any suppored
+The C<register_hook> function should be used to register any supported
 hooks the plugin needs.  It returns 0 on success, 1 if the hook
 type is not supported and -1 if the major version in C<struct hook>
 does not match the front end's major hook API version.
@@ -979,7 +991,7 @@ itself but the I<value> might.
 
 Any (non-comment) strings immediately after the plugin path are
 treated as arguments to the plugin.  These arguments are split on
-a whitespace boundary and are passed to the plugin in the form of
+a white space boundary and are passed to the plugin in the form of
 a C<NULL>-terminated array of strings.  If no arguments were
 specified, I<plugin_args> will be the NULL pointer.
 
@@ -1183,13 +1195,6 @@ be reflected in the version of the environment that is used to
 execute a command.  A future version of the API will support
 hooking internal B<sudo> front end functions as well.
 
-Environment-related hooks are disabled prior to the execution of
-the C<init_session> policy plugin function (if any).  This is
-necessary because C<init_session> has no way of passing back a
-modified environment pointer.  However, since the user environment
-specified by the C<check_policy> function is already in place, there
-should be no need to hook the environment functions at that time.
-
 =head3 Hook structure
 
 Hooks in B<sudo> are described by the following structure:
index 36d23d2988b0759bbc3d4396ab098fade43331eb..828b8507944d5512e84399fffc9d21df2c62c0ea 100644 (file)
@@ -127,7 +127,7 @@ struct policy_plugin {
        const char *list_user);
     int (*validate)(void);
     void (*invalidate)(int remove);
-    int (*init_session)(struct passwd *pwd);
+    int (*init_session)(struct passwd *pwd, char **user_env_out[]);
     void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
     void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
 };
index 74a7ea55c980e44806366553017b9e898271d700..5006e4badc3fcdb8e8bb5818a9eb52c5881b3315 100644 (file)
@@ -196,7 +196,7 @@ sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth)
 }
 
 int
-sudo_pam_begin_session(struct passwd *pw, sudo_auth *auth)
+sudo_pam_begin_session(struct passwd *pw, char **user_envp[], sudo_auth *auth)
 {
     int status = PAM_SUCCESS;
     debug_decl(sudo_pam_begin_session, SUDO_DEBUG_AUTH)
@@ -230,6 +230,26 @@ sudo_pam_begin_session(struct passwd *pw, sudo_auth *auth)
      */
     (void) pam_setcred(pamh, PAM_ESTABLISH_CRED);
 
+#ifdef HAVE_PAM_GETENVLIST
+    /*
+     * Update environment based on what is stored in pamh.
+     * If no authentication is done we will only have environment
+     * variables if pam_env is called via session.
+     */
+    if (user_envp != NULL) {
+       char **pam_envp = pam_getenvlist(pamh);
+       if (pam_envp != NULL) {
+           /* Merge pam env with user env but do not overwrite. */
+           env_init(*user_envp);
+           env_merge(pam_envp, false);
+           *user_envp = env_get();
+           env_init(NULL);
+           efree(pam_envp);
+           /* XXX - we leak any duplicates that were in pam_envp */
+       }
+    }
+#endif /* HAVE_PAM_GETENVLIST */
+
 #ifndef NO_PAM_SESSION
     status = pam_open_session(pamh, 0);
     if (status != PAM_SUCCESS) {
index dded9cc531dc6b017c9d82e43f76cdc860cedee8..b914f39734a187e2d20b4e165f1357aaa0007330 100644 (file)
@@ -286,7 +286,7 @@ done:
 }
 
 int
-sudo_auth_begin_session(struct passwd *pw)
+sudo_auth_begin_session(struct passwd *pw, char **user_env[])
 {
     sudo_auth *auth;
     int status;
@@ -294,7 +294,7 @@ sudo_auth_begin_session(struct passwd *pw)
 
     for (auth = auth_switch; auth->name; auth++) {
        if (auth->begin_session && !IS_DISABLED(auth)) {
-           status = (auth->begin_session)(pw, auth);
+           status = (auth->begin_session)(pw, user_env, auth);
            if (status == AUTH_FATAL) {
                /* XXX log */
                audit_failure(NewArgv, "authentication failure");
index 7259f22b5ddd5fe02309b8e3078fff601a036a24..a7836610a6fa9fb6c55c65557e207dc8d5620daf 100644 (file)
@@ -32,7 +32,7 @@ typedef struct sudo_auth {
     int (*setup)(struct passwd *pw, char **prompt, struct sudo_auth *auth);
     int (*verify)(struct passwd *pw, char *p, struct sudo_auth *auth);
     int (*cleanup)(struct passwd *pw, struct sudo_auth *auth);
-    int (*begin_session)(struct passwd *pw, struct sudo_auth *auth);
+    int (*begin_session)(struct passwd *pw, char **user_env[], struct sudo_auth *auth);
     int (*end_session)(struct passwd *pw, struct sudo_auth *auth);
 } sudo_auth;
 
@@ -66,7 +66,7 @@ int sudo_fwtk_cleanup(struct passwd *pw, sudo_auth *auth);
 int sudo_pam_init(struct passwd *pw, sudo_auth *auth);
 int sudo_pam_verify(struct passwd *pw, char *prompt, sudo_auth *auth);
 int sudo_pam_cleanup(struct passwd *pw, sudo_auth *auth);
-int sudo_pam_begin_session(struct passwd *pw, sudo_auth *auth);
+int sudo_pam_begin_session(struct passwd *pw, char **user_env[], sudo_auth *auth);
 int sudo_pam_end_session(struct passwd *pw, sudo_auth *auth);
 int sudo_securid_init(struct passwd *pw, sudo_auth *auth);
 int sudo_securid_setup(struct passwd *pw, char **prompt, sudo_auth *auth);
index d366e72d96134a949dc3c40c88ad717173ea4b8b..3d2af7ffcc9d7b9a112f92d4ed7cc648870c1c88 100644 (file)
@@ -90,6 +90,7 @@
 #define KEPT_MAX       0xff00
 
 struct environment {
+    char * const *old_envp;    /* pointer the environment we passed back */
     char **envp;               /* pointer to the new environment */
     size_t env_size;           /* size of new_environ in char **'s */
     size_t env_len;            /* number of slots used, not counting NULL */
@@ -208,8 +209,10 @@ env_init(char * const envp[])
     debug_decl(env_init, SUDO_DEBUG_ENV)
 
     if (envp == NULL) {
-       /* Reset to initial state. */
+       /* Reset to initial state but keep a pointer to what we allocated. */
+       envp = env.envp;
        memset(&env, 0, sizeof(env));
+       env.old_envp = envp;
     } else {
        /* Make private copy of envp. */
        for (ep = envp; *ep != NULL; ep++)
@@ -224,6 +227,10 @@ env_init(char * const envp[])
 #endif
        memcpy(env.envp, envp, len * sizeof(char *));
        env.envp[len] = '\0';
+
+       /* Free the old envp we allocated, if any. */
+       if (env.old_envp != NULL)
+           efree((void *)env.old_envp);
     }
 
     debug_return;
@@ -485,6 +492,21 @@ sudo_getenv(const char *name)
     debug_return_str(val);
 }
 
+/*
+ * Merge another environment with our private copy.
+ */
+void
+env_merge(char * const envp[], bool overwrite)
+{
+    char * const *ep;
+    debug_decl(env_merge, SUDO_DEBUG_ENV)
+
+    for (ep = envp; *ep != NULL; ep++)
+       sudo_putenv(*ep, true, overwrite);
+
+    debug_return;
+}
+
 /*
  * Check the env_delete blacklist.
  * Returns true if the variable was found, else false.
index 33f2dcfe2bc6bae0a4678548c1a972ee5940f610..7b35d2863e6493bd54109c2fa61f7509ee25e419 100644 (file)
@@ -122,6 +122,7 @@ sudo_conv_t sudo_conv;
 sudo_printf_t sudo_printf;
 int sudo_mode;
 
+static int sudo_version;
 static char *prev_user;
 static char *runas_user;
 static char *runas_group;
@@ -146,15 +147,16 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation,
     struct sudo_nss *nss;
     debug_decl(sudoers_policy_open, SUDO_DEBUG_PLUGIN)
 
-    /* Plugin args are only specified for API version 1.2 and higher. */
-    if (version < SUDO_API_MKVERSION(1, 2))
-       args = NULL;
-
+    sudo_version = version;
     if (!sudo_conv)
        sudo_conv = conversation;
     if (!sudo_printf)
        sudo_printf = plugin_printf;
 
+    /* Plugin args are only specified for API version 1.2 and higher. */
+    if (sudo_version < SUDO_API_MKVERSION(1, 2))
+       args = NULL;
+
     if (sigsetjmp(error_jmp, 1)) {
        /* called via error(), errorx() or log_error() */
        rewind_perms();
@@ -281,16 +283,20 @@ sudoers_policy_close(int exit_status, int error_code)
  * and before uid/gid changes occur.
  */
 static int
-sudoers_policy_init_session(struct passwd *pwd)
+sudoers_policy_init_session(struct passwd *pwd, char **user_env[])
 {
     debug_decl(sudoers_policy_init, SUDO_DEBUG_PLUGIN)
 
+    /* user_env is only specified for API version 1.2 and higher. */
+    if (sudo_version < SUDO_API_MKVERSION(1, 2))
+       user_env = NULL;
+
     if (sigsetjmp(error_jmp, 1)) {
        /* called via error(), errorx() or log_error() */
        return -1;
     }
 
-    debug_return_bool(sudo_auth_begin_session(pwd));
+    debug_return_bool(sudo_auth_begin_session(pwd, user_env));
 }
 
 static int
index 07a548128ab1ab0037f9ad6436df82403651d5ff..7f753efefcc1dd84811144c8db8d76d7f976b3f6 100644 (file)
@@ -215,9 +215,9 @@ void remove_timestamp(bool);
 bool user_is_exempt(void);
 
 /* sudo_auth.c */
-int verify_user(struct passwd *, char *);
-int sudo_auth_begin_session(struct passwd *);
-int sudo_auth_end_session(struct passwd *);
+int verify_user(struct passwd *pw, char *prompt);
+int sudo_auth_begin_session(struct passwd *pw, char **user_env[]);
+int sudo_auth_end_session(struct passwd *pw);
 int sudo_auth_init(struct passwd *pw);
 int sudo_auth_cleanup(struct passwd *pw);
 
@@ -304,6 +304,7 @@ char *expand_iolog_path(const char *prefix, const char *dir, const char *file,
 
 /* env.c */
 char **env_get(void);
+void env_merge(char * const envp[], bool overwrite);
 void env_init(char * const envp[]);
 void init_envtables(void);
 void insert_env_vars(char * const envp[]);
index 4f694cc8f4e903900a48f4896074b1d2145ddca3..5e8a395edc6ec2be0321ab4d7227f77f2b395cdf 100644 (file)
@@ -278,29 +278,3 @@ deregister_hook(struct sudo_hook *hook)
 
     debug_return_int(rval);
 }
-
-/* Deregister all environment handling hooks. */
-void
-deregister_env_hooks(void)
-{
-    struct sudo_hook_list *tofree;
-    debug_decl(deregister_env_hooks, SUDO_DEBUG_HOOKS)
-
-    while ((tofree = sudo_hook_setenv_list) != NULL) {
-       sudo_hook_setenv_list = sudo_hook_setenv_list->next;
-       efree(tofree);
-    }
-    while ((tofree = sudo_hook_unsetenv_list) != NULL) {
-       sudo_hook_unsetenv_list = sudo_hook_unsetenv_list->next;
-       efree(tofree);
-    }
-    while ((tofree = sudo_hook_getenv_list) != NULL) {
-       sudo_hook_getenv_list = sudo_hook_getenv_list->next;
-       efree(tofree);
-    }
-    while ((tofree = sudo_hook_putenv_list) != NULL) {
-       sudo_hook_putenv_list = sudo_hook_putenv_list->next;
-       efree(tofree);
-    }
-    debug_return;
-}
index 16c300343865096ca2c5e21df6ae4719b958b180..d075566ea3b02f0c81ec7ea9ae0846b47c2f9cb0 100644 (file)
@@ -130,7 +130,7 @@ static int policy_list(struct plugin_container *plugin, int argc,
 static int policy_validate(struct plugin_container *plugin);
 static void policy_invalidate(struct plugin_container *plugin, int remove);
 static int policy_init_session(struct plugin_container *plugin,
-    struct passwd *pwd);
+    struct passwd *pwd, char **user_env[]);
 
 /* I/O log plugin convenience functions. */
 static int iolog_open(struct plugin_container *plugin, char * const settings[],
@@ -148,8 +148,6 @@ static struct rlimit corelimit;
 static struct rlimit nproclimit;
 #endif
 
-extern char **environ;
-
 int
 main(int argc, char *argv[], char *envp[])
 {
@@ -285,9 +283,6 @@ main(int argc, char *argv[], char *envp[])
                        plugin->name);
                }
            }
-           /* Now that we have the command's environment, disable env hooks. */
-           deregister_env_hooks();
-
            /* Setup command details and run command/edit. */
            command_info_to_details(command_info, &command_details);
            command_details.argv = argv_out;
@@ -850,17 +845,11 @@ exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
     aix_restoreauthdb();
 #endif
 
-    /*
-     * Swap in the plugin-supplied environment in case session init
-     * modifies the environment.  This is kind of a hack.
-     */
-    environ = details->envp;
-
     /*
      * Call policy plugin's session init before other setup occurs.
      * The session init code is expected to print an error as needed.
      */
-    if (policy_init_session(&policy_plugin, pw) != true)
+    if (policy_init_session(&policy_plugin, pw, &details->envp) != true)
        goto done;
 
 #ifdef HAVE_SELINUX
@@ -912,9 +901,6 @@ exec_setup(struct command_details *details, const char *ptyname, int ptyfd)
 #endif /* HAVE_LOGIN_CAP_H */
     }
 
-    /* Update the environment pointer in command details */
-    details->envp = environ;
-
     /*
      * Set groups, including supplementary group vector.
      */
@@ -1148,12 +1134,25 @@ policy_invalidate(struct plugin_container *plugin, int remove)
 }
 
 static int
-policy_init_session(struct plugin_container *plugin, struct passwd *pwd)
+policy_init_session(struct plugin_container *plugin, struct passwd *pwd, char **user_env[])
 {
+    int rval = true;
     debug_decl(policy_init_session, SUDO_DEBUG_PCOMM)
-    if (plugin->u.policy->init_session)
-       debug_return_bool(plugin->u.policy->init_session(pwd));
-    debug_return_bool(true);
+
+    if (plugin->u.policy->init_session) {
+       /*
+        * Backwards compatibility for older API versions
+        */
+       switch (plugin->u.generic->version) {
+       case SUDO_API_MKVERSION(1, 0):
+       case SUDO_API_MKVERSION(1, 1):
+           rval = plugin->u.policy_1_0->init_session(pwd);
+           break;
+       default:
+           rval = plugin->u.policy->init_session(pwd, user_env);
+       }
+    }
+    debug_return_bool(rval);
 }
 
 static int
index c72f65274a374cffdb66ad8a07d3b52247c3b9e9..ec981132fd594be0de108f792599911058a784f7 100644 (file)
@@ -231,7 +231,6 @@ int process_hooks_getenv(const char *name, char **val);
 int process_hooks_setenv(const char *name, const char *value, int overwrite);
 int process_hooks_putenv(char *string);
 int process_hooks_unsetenv(const char *name);
-void deregister_env_hooks(void);
 
 /* interfaces.c */
 int get_net_ifs(char **addrinfo);