From 31087fdcd66a37aea9f96dafc617bec83c898825 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sat, 15 May 2010 07:51:24 -0400 Subject: [PATCH] Add sudoedit support --- plugins/sample/sample_plugin.c | 182 +++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 7 deletions(-) diff --git a/plugins/sample/sample_plugin.c b/plugins/sample/sample_plugin.c index 3c0d6079c..94980be24 100644 --- a/plugins/sample/sample_plugin.c +++ b/plugins/sample/sample_plugin.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ +#include #include #include #include @@ -51,6 +53,7 @@ #include #include #include +#include /* * Sample plugin module that allows any user who knows the password @@ -168,6 +171,41 @@ policy_open(unsigned int version, sudo_conv_t conversation, return 1; } +static char * +find_in_path(char *command, char **envp) +{ + struct stat sb; + char *path, *path0, **ep, *cp; + char pathbuf[PATH_MAX], *qualified = NULL; + + if (strchr(command, '/') != NULL) + return command; + + path = _PATH_DEFPATH; + for (ep = plugin_state.envp; *ep != NULL; ep++) { + if (strncmp(*ep, "PATH=", 5) == 0) { + path = *ep + 5; + break; + } + } + path = path0 = strdup(path); + do { + if ((cp = strchr(path, ':'))) + *cp = '\0'; + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", *path ? path : ".", + command); + if (stat(pathbuf, &sb) == 0) { + if (S_ISREG(sb.st_mode) && (sb.st_mode & 0000111)) { + qualified = pathbuf; + break; + } + } + path = cp + 1; + } while (cp != NULL); + free(path0); + return qualified ? strdup(qualified) : NULL; +} + /* * Plugin policy check function. * Simple example that prompts for a password, hard-coded to "test". @@ -179,17 +217,17 @@ policy_check(int argc, char * const argv[], { struct sudo_conv_message msg; struct sudo_conv_reply repl; - char **command_info; + char **command_info, *command; int i = 0; if (!argc || argv[0] == NULL) { sudo_log(SUDO_CONV_ERROR_MSG, "no command specified\n"); return FALSE; } - /* Only allow fully qualified paths to keep things simple. */ - if (argv[0][0] != '/') { - sudo_log(SUDO_CONV_ERROR_MSG, - "only fully qualified pathnames may be specified\n"); + + command = find_in_path(argv[0], plugin_state.envp); + if (command == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "%s: command not found\n", argv[0]); return FALSE; } @@ -218,7 +256,137 @@ policy_check(int argc, char * const argv[], sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); return ERROR; } - if ((command_info[i++] = fmt_string("command", argv[0])) == NULL || + if ((command_info[i++] = fmt_string("command", command)) == NULL || + asprintf(&command_info[i++], "runas_euid=%ld", (long)runas_uid) == -1 || + asprintf(&command_info[i++], "runas_uid=%ld", (long)runas_uid) == -1) { + sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); + return ERROR; + } + if (runas_gid != -1) { + if (asprintf(&command_info[i++], "runas_gid=%ld", (long)runas_gid) == -1 || + asprintf(&command_info[i++], "runas_egid=%ld", (long)runas_gid) == -1) { + sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); + return ERROR; + } + } +#ifdef USE_TIMEOUT + command_info[i++] = "timeout=30"; +#endif + + *command_info_out = command_info; + + return TRUE; +} + +static char * +find_editor(int nfiles, char * const files[], char **argv_out[]) +{ + char *cp, **ep, **nargv, *editor, *editor_path; + int ac, i, nargc, wasblank; + + /* Lookup EDITOR in user's environment. */ + editor = _PATH_VI; + for (ep = plugin_state.envp; *ep != NULL; ep++) { + if (strncmp(*ep, "EDITOR=", 7) == 0) { + editor = *ep + 7; + break; + } + } + editor = strdup(editor); + if (editor == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "unable to allocate memory\n"); + return NULL; + } + + /* + * Split editor into an argument vector; editor is reused (do not free). + * The EDITOR environment variables may contain command + * line args so look for those and alloc space for them too. + */ + nargc = 1; + for (wasblank = 0, cp = editor; *cp != '\0'; cp++) { + if (isblank((unsigned char) *cp)) + wasblank = 1; + else if (wasblank) { + wasblank = 0; + nargc++; + } + } + /* If we can't find the editor in the user's PATH, give up. */ + cp = strtok(editor, " \t"); + if (cp == NULL || + (editor_path = find_in_path(editor, plugin_state.envp)) == NULL) { + return NULL; + } + nargv = (char **) malloc((nargc + 1 + nfiles + 1) * sizeof(char *)); + if (nargv == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "unable to allocate memory\n"); + return NULL; + } + for (ac = 0; cp != NULL && ac < nargc; ac++) { + nargv[ac] = cp; + cp = strtok(NULL, " \t"); + } + nargv[ac++] = "--"; + for (i = 0; i < nfiles; ) + nargv[ac++] = files[i++]; + nargv[ac] = NULL; + + *argv_out = nargv; + return editor_path; +} + +/* + * Plugin policy edit function. + * Simple example that prompts for a password, hard-coded to "test". + */ +static int +policy_edit(int argc, char * const argv[], + char *env_add[], char **command_info_out[], + char **argv_out[], char **user_env_out[]) +{ + struct sudo_conv_message msg; + struct sudo_conv_reply repl; + char **command_info, *editor; + int i = 0; + + if (!argc || argv[0] == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "no command specified\n"); + return FALSE; + } + + /* Prompt user for password via conversation function. */ + memset(&msg, 0, sizeof(msg)); + msg.msg_type = SUDO_CONV_PROMPT_ECHO_OFF; + msg.msg = "Password: "; + memset(&repl, 0, sizeof(repl)); + sudo_conv(1, &msg, &repl); + if (repl.reply == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "missing password\n"); + return FALSE; + } + if (strcmp(repl.reply, "test") != 0) { + sudo_log(SUDO_CONV_ERROR_MSG, "incorrect password\n"); + return FALSE; + } + + /* Rebuild argv using editor */ + editor = find_editor(argc - 1, argv + 1, argv_out); + if (editor == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "unable to find valid editor\n"); + return ERROR; + } + + /* No changes envp */ + *user_env_out = plugin_state.envp; + + /* Setup command info. */ + command_info = calloc(32, sizeof(char *)); + if (command_info == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); + return ERROR; + } + if ((command_info[i++] = fmt_string("command", editor)) == NULL || asprintf(&command_info[i++], "runas_euid=%ld", (long)runas_uid) == -1 || asprintf(&command_info[i++], "runas_uid=%ld", (long)runas_uid) == -1) { sudo_log(SUDO_CONV_ERROR_MSG, "out of memory\n"); @@ -347,7 +515,7 @@ struct policy_plugin sample_policy = { policy_list, NULL, /* validate */ NULL, /* invalidate */ - NULL /* sudoedit */ + policy_edit }; /* -- 2.40.0