plugins/sudoers/def_data.in
plugins/sudoers/defaults.c
plugins/sudoers/defaults.h
+plugins/sudoers/editor.c
plugins/sudoers/env.c
plugins/sudoers/find_path.c
plugins/sudoers/getdate.c
redblack.lo sudoers_debug.lo timestr.lo toke.lo \
toke_util.lo
-SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo env.lo find_path.lo \
+SUDOERS_OBJS = $(AUTH_OBJS) boottime.lo check.lo editor.lo env.lo find_path.lo \
goodpath.lo group_plugin.lo interfaces.lo iolog.lo \
iolog_path.lo locale.lo logging.lo logwrap.lo parse.lo \
policy.lo prompt.lo set_perms.lo sudo_nss.lo sudoers.lo \
timestamp.lo @SUDOERS_OBJS@
-VISUDO_OBJS = find_path.o goodpath.o locale.o sudo_printf.o visudo.o \
+VISUDO_OBJS = editor.o find_path.o goodpath.o locale.o sudo_printf.o visudo.o \
visudo_json.o
REPLAY_OBJS = getdate.o locale.o sudoreplay.o
$(srcdir)/sudoers.h $(srcdir)/sudoers_debug.h \
$(top_builddir)/config.h $(top_builddir)/pathnames.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/defaults.c
+editor.lo: $(srcdir)/editor.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
+ $(incdir)/sudo_compat.h $(incdir)/sudo_conf.h \
+ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
+ $(incdir)/sudo_gettext.h $(incdir)/sudo_plugin.h \
+ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h $(srcdir)/defaults.h \
+ $(srcdir)/logging.h $(srcdir)/sudo_nss.h $(srcdir)/sudoers.h \
+ $(srcdir)/sudoers_debug.h $(top_builddir)/config.h \
+ $(top_builddir)/pathnames.h
+ $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/editor.c
+editor.o: editor.lo
env.lo: $(srcdir)/env.c $(devdir)/def_data.h $(incdir)/compat/stdbool.h \
$(incdir)/sudo_compat.h $(incdir)/sudo_conf.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_fatal.h $(incdir)/sudo_gettext.h \
--- /dev/null
+/*
+ * Copyright (c) 2010-2015 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
+# include <memory.h>
+# endif
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <errno.h>
+
+#include "sudoers.h"
+
+/*
+ * Search for the specified editor in the user's PATH, checking
+ * the result against whitelist if non-NULL. An argument vector
+ * suitable for execve() is allocated and stored in argv_out.
+ * If nfiles is non-zero, files[] is added to the end of argv_out.
+ * Returns the path to be executed on success, else NULL.
+ * The caller is responsible for freeing the returned editor path
+ * as well as the argument vector.
+ */
+char *
+resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
+ int *argc_out, char ***argv_out, char * const *whitelist)
+{
+ char **nargv, *editor, *editor_path = NULL;
+ const char *cp, *ep, *tmp;
+ const char *edend = ed + edlen;
+ struct stat user_editor_sb;
+ int nargc;
+ debug_decl(resolve_editor, SUDOERS_DEBUG_UTIL)
+
+ /*
+ * Split editor into an argument vector, including files to edit.
+ * The EDITOR and VISUAL environment variables may contain command
+ * line args so look for those and alloc space for them too.
+ */
+ cp = sudo_strsplit(ed, edend, " \t", &ep);
+ if (cp == NULL)
+ debug_return_str(NULL);
+ editor = strndup(cp, (size_t)(ep - cp));
+ if (editor == NULL) {
+ sudo_warnx(U_("unable to allocate memory"));
+ debug_return_str(NULL);
+ }
+
+ /* If we can't find the editor in the user's PATH, give up. */
+ if (find_path(editor, &editor_path, &user_editor_sb, getenv("PATH"), 0, whitelist) != FOUND) {
+ free(editor);
+ errno = ENOENT;
+ debug_return_str(NULL);
+ }
+
+ /* Count rest of arguments and allocate editor argv. */
+ for (nargc = 1, tmp = ep; sudo_strsplit(NULL, edend, " \t", &tmp) != NULL; )
+ nargc++;
+ if (nfiles != 0)
+ nargc += nfiles + 1;
+ nargv = reallocarray(NULL, nargc + 1, sizeof(char *));
+ if (nargv == NULL) {
+ sudo_warnx(U_("unable to allocate memory"));
+ free(editor);
+ debug_return_str(NULL);
+ }
+
+ /* Fill in editor argv (assumes files[] is NULL-terminated). */
+ nargv[0] = editor;
+ for (nargc = 1; (cp = sudo_strsplit(NULL, edend, " \t", &ep)) != NULL; nargc++) {
+ nargv[nargc] = strndup(cp, (size_t)(ep - cp));
+ if (nargv[nargc] == NULL) {
+ sudo_warnx(U_("unable to allocate memory"));
+ while (nargc--)
+ free(nargv[nargc]);
+ debug_return_str(NULL);
+ }
+ }
+ if (nfiles != 0) {
+ nargv[nargc++] = "--";
+ while (nfiles--)
+ nargv[nargc++] = *files++;
+ }
+ nargv[nargc] = NULL;
+
+ *argc_out = nargc;
+ *argv_out = nargv;
+ debug_return_str(editor_path);
+}
#include "sudoers.h"
+/*
+ * Check the given command against the specified whitelist (NULL-terminated).
+ * On success, rewrites cmnd based on the whitelist and returns true.
+ * On failure, returns false.
+ */
+static bool
+cmnd_allowed(char *cmnd, size_t cmnd_size, struct stat *cmnd_sbp,
+ char * const *whitelist)
+{
+ const char *cmnd_base;
+ char * const *wl;
+ debug_decl(cmnd_allowed, SUDOERS_DEBUG_UTIL)
+
+ if (!sudo_goodpath(cmnd, cmnd_sbp))
+ debug_return_bool(false);
+
+ if (whitelist == NULL)
+ debug_return_bool(true); /* nothing to check */
+
+ /* We compare the base names to avoid excessive stat()ing. */
+ if ((cmnd_base = strrchr(cmnd, '/')) == NULL)
+ debug_return_bool(false); /* can't happen */
+ cmnd_base++;
+
+ for (wl = whitelist; *wl != NULL; wl++) {
+ struct stat sb;
+ const char *base;
+
+ if ((base = strrchr(*wl, '/')) == NULL)
+ continue; /* XXX - warn? */
+ base++;
+
+ if (strcmp(cmnd_base, base) != 0)
+ continue;
+
+ if (sudo_goodpath(*wl, &sb) &&
+ sb.st_dev == cmnd_sbp->st_dev && sb.st_ino == cmnd_sbp->st_ino) {
+ /* Overwrite cmnd with safe version from whitelist. */
+ if (strlcpy(cmnd, *wl, cmnd_size) < cmnd_size)
+ return true;
+ debug_return_bool(true);
+ }
+ }
+ debug_return_bool(false);
+}
+
/*
* This function finds the full pathname for a command and
* stores it in a statically allocated array, filling in a pointer
* to the array. Returns FOUND if the command was found, NOT_FOUND
* if it was not found, or NOT_FOUND_DOT if it would have been found
* but it is in '.' and IGNORE_DOT is set.
+ * The caller is responsible for freeing the output file.
*/
int
find_path(const char *infile, char **outfile, struct stat *sbp,
- const char *path, int ignore_dot)
+ const char *path, int ignore_dot, char * const *whitelist)
{
- static char command[PATH_MAX];
+ char command[PATH_MAX];
const char *cp, *ep, *pathend;
bool found = false;
bool checkdot = false;
int len;
debug_decl(find_path, SUDOERS_DEBUG_UTIL)
- if (strlen(infile) >= PATH_MAX) {
- errno = ENAMETOOLONG;
- debug_return_int(NOT_FOUND_ERROR);
- }
-
/*
* If we were given a fully qualified or relative path
* there is no need to look at $PATH.
*/
if (strchr(infile, '/') != NULL) {
- strlcpy(command, infile, sizeof(command)); /* paranoia */
- if (sudo_goodpath(command, sbp)) {
- *outfile = command;
- debug_return_int(FOUND);
- } else
- debug_return_int(NOT_FOUND);
+ if (strlcpy(command, infile, sizeof(command)) >= sizeof(command)) {
+ errno = ENAMETOOLONG;
+ debug_return_int(NOT_FOUND_ERROR);
+ }
+ found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
+ goto done;
}
if (path == NULL)
errno = ENAMETOOLONG;
debug_return_int(NOT_FOUND_ERROR);
}
- if ((found = sudo_goodpath(command, sbp)))
+ found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
+ if (found)
break;
}
errno = ENAMETOOLONG;
debug_return_int(NOT_FOUND_ERROR);
}
- found = sudo_goodpath(command, sbp);
+ found = cmnd_allowed(command, sizeof(command), sbp, whitelist);
if (found && ignore_dot)
debug_return_int(NOT_FOUND_DOT);
}
+done:
if (found) {
- *outfile = command;
+ if ((*outfile = strdup(command)) == NULL)
+ debug_return_int(NOT_FOUND_ERROR);
debug_return_int(FOUND);
- } else
- debug_return_int(NOT_FOUND);
+ }
+ debug_return_int(NOT_FOUND);
}
bool
sudo_goodpath(const char *path, struct stat *sbp)
{
- struct stat sb;
bool rval = false;
debug_decl(sudo_goodpath, SUDOERS_DEBUG_UTIL)
- if (path != NULL && stat(path, &sb) == 0) {
- /* Make sure path describes an executable regular file. */
- if (S_ISREG(sb.st_mode) && ISSET(sb.st_mode, 0111))
- rval = true;
- else
- errno = EACCES;
- if (sbp)
- (void) memcpy(sbp, &sb, sizeof(struct stat));
+ if (path != NULL) {
+ struct stat sb;
+
+ if (sbp == NULL)
+ sbp = &sb;
+
+ if (stat(path, sbp) == 0) {
+ /* Make sure path describes an executable regular file. */
+ if (S_ISREG(sbp->st_mode) && ISSET(sbp->st_mode, 0111))
+ rval = true;
+ else
+ errno = EACCES;
+ }
}
debug_return_bool(rval);
int edit_argc;
free(safe_cmnd);
- safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc, &edit_argv);
+ safe_cmnd = find_editor(NewArgc - 1, NewArgv + 1, &edit_argc,
+ &edit_argv);
if (safe_cmnd == NULL || audit_success(edit_argc, edit_argv) != 0)
goto bad;
if (!set_perms(PERM_RUNAS))
debug_return_int(-1);
rval = find_path(NewArgv[0], &user_cmnd, user_stat, path,
- def_ignore_dot);
+ def_ignore_dot, NULL);
if (!restore_perms())
debug_return_int(-1);
if (rval == NOT_FOUND) {
if (!set_perms(PERM_USER))
debug_return_int(-1);
rval = find_path(NewArgv[0], &user_cmnd, user_stat, path,
- def_ignore_dot);
+ def_ignore_dot, NULL);
if (!restore_perms())
debug_return_int(-1);
}
debug_return;
}
-static char *
-resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
- int *argc_out, char ***argv_out)
-{
- char **nargv, *editor, *editor_path = NULL;
- const char *cp, *ep, *tmp;
- const char *edend = ed + edlen;
- int nargc;
- debug_decl(resolve_editor, SUDOERS_DEBUG_PLUGIN)
-
- /*
- * Split editor into an argument vector, including files to edit.
- * The EDITOR and VISUAL environment variables may contain command
- * line args so look for those and alloc space for them too.
- */
- cp = sudo_strsplit(ed, edend, " \t", &ep);
- if (cp == NULL)
- debug_return_str(NULL);
- editor = strndup(cp, (size_t)(ep - cp));
- if (editor == NULL) {
- sudo_warnx(U_("unable to allocate memory"));
- debug_return_str(NULL);
- }
-
- /* If we can't find the editor in the user's PATH, give up. */
- if (find_path(editor, &editor_path, NULL, getenv("PATH"), 0) != FOUND) {
- free(editor);
- errno = ENOENT;
- debug_return_str(NULL);
- }
-
- /* Count rest of arguments and allocate editor argv. */
- for (nargc = 1, tmp = ep; sudo_strsplit(NULL, edend, " \t", &tmp) != NULL; )
- nargc++;
- nargv = reallocarray(NULL, nargc + 1 + nfiles + 1, sizeof(char *));
- if (nargv == NULL) {
- sudo_warnx(U_("unable to allocate memory"));
- free(editor);
- debug_return_str(NULL);
- }
-
- /* Fill in editor argv (assumes files[] is NULL-terminated). */
- nargv[0] = editor;
- for (nargc = 1; (cp = sudo_strsplit(NULL, edend, " \t", &ep)) != NULL; nargc++) {
- nargv[nargc] = strndup(cp, (size_t)(ep - cp));
- if (nargv[nargc] == NULL) {
- sudo_warnx(U_("unable to allocate memory"));
- while (nargc--)
- free(nargv[nargc]);
- debug_return_str(NULL);
- }
- }
- nargv[nargc++] = "--";
- while ((nargv[nargc++] = *files++) != NULL)
- continue;
-
- *argc_out = nargc;
- *argv_out = nargv;
- debug_return_str(editor_path);
-}
-
/*
* Determine which editor to use. We don't need to worry about restricting
* this to a "safe" editor since it runs with the uid of the invoking user,
* not the runas (privileged) user.
+ * Returns a fully-qualified path to the editor on success and fills
+ * in argc_out and argv_out accordingly. Returns NULL on failure.
*/
static char *
find_editor(int nfiles, char **files, int *argc_out, char ***argv_out)
{
- const char *cp, *ep, *editor;
+ const char *cp, *ep, *editor = NULL;
char *editor_path = NULL, **ev, *ev0[4];
- size_t len;
debug_decl(find_editor, SUDOERS_DEBUG_PLUGIN)
/*
ev0[3] = NULL;
for (ev = ev0; editor_path == NULL && *ev != NULL; ev++) {
if ((editor = getenv(*ev)) != NULL && *editor != '\0') {
- editor_path = resolve_editor(editor, strlen(editor), nfiles,
- files, argc_out, argv_out);
+ editor_path = resolve_editor(editor, strlen(editor),
+ nfiles, files, argc_out, argv_out, NULL);
if (editor_path == NULL && errno != ENOENT)
debug_return_str(NULL);
}
}
if (editor_path == NULL) {
/* def_editor could be a path, split it up, avoiding strtok() */
- cp = editor = def_editor;
- do {
- if ((ep = strchr(cp, ':')) != NULL)
- len = ep - cp;
- else
- len = strlen(cp);
- editor_path = resolve_editor(cp, len, nfiles, files, argc_out, argv_out);
+ const char *def_editor_end = def_editor + strlen(def_editor);
+ for (cp = sudo_strsplit(def_editor, def_editor_end, ":", &ep);
+ cp != NULL; cp = sudo_strsplit(NULL, def_editor_end, ":", &ep)) {
+ editor_path = resolve_editor(cp, (size_t)(ep - cp), nfiles,
+ files, argc_out, argv_out, NULL);
if (editor_path == NULL && errno != ENOENT)
debug_return_str(NULL);
- cp = ep + 1;
} while (ep != NULL && editor_path == NULL);
}
if (!editor_path) {
- audit_failure(NewArgc, NewArgv, N_("%s: command not found"), editor);
- sudo_warnx(U_("%s: command not found"), editor);
+ audit_failure(NewArgc, NewArgv, N_("%s: command not found"),
+ editor ? editor : def_editor);
+ sudo_warnx(U_("%s: command not found"), editor ? editor : def_editor);
}
debug_return_str(editor_path);
}
#define NOT_FOUND 1
#define NOT_FOUND_DOT 2
#define NOT_FOUND_ERROR 3
+#define NOT_FOUND_PATH 4
/*
* Various modes sudo can be in (based on arguments) in hex
#define YY_DECL int sudoerslex(void)
/* goodpath.c */
-bool sudo_goodpath(const char *, struct stat *);
+bool sudo_goodpath(const char *path, struct stat *sbp);
/* findpath.c */
-int find_path(const char *, char **, struct stat *, const char *, int);
+int find_path(const char *infile, char **outfile, struct stat *sbp,
+ const char *path, int ignore_dot, char * const *whitelist);
/* check.c */
int check_user(int validate, int mode);
int group_plugin_query(const char *user, const char *group,
const struct passwd *pwd);
+/* editor.c */
+char *resolve_editor(const char *ed, size_t edlen, int nfiles, char **files,
+ int *argc_out, char ***argv_out, char * const *whitelist);
+
#endif /* SUDOERS_SUDOERS_H */
* Function prototypes
*/
static void quit(int);
-static char *get_args(char *);
-static char *get_editor(char **);
static void get_hostname(void);
static int whatnow(void);
static int check_aliases(bool, bool);
+static char *get_editor(int *editor_argc, char ***editor_argv);
static bool check_syntax(const char *, bool, bool, bool);
-static bool edit_sudoers(struct sudoersfile *, char *, char *, int);
+static bool edit_sudoers(struct sudoersfile *, char *, int, char **, int);
static bool install_sudoers(struct sudoersfile *, bool);
static int print_unused(void *, void *);
-static bool reparse_sudoers(char *, char *, bool, bool);
+static bool reparse_sudoers(char *, int, char **, bool, bool);
static int run_command(char *, char **);
static void parse_sudoers_options(void);
static void setup_signals(void);
main(int argc, char *argv[])
{
struct sudoersfile *sp;
- char *args, *editor;
- int ch, exitcode = 0;
+ char *editor, **editor_argv;
+ int ch, editor_argc, exitcode = 0;
bool quiet, strict, oldperms;
const char *export_path;
debug_decl(main, SUDOERS_DEBUG_MAIN)
sudoersparse();
(void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER);
- editor = get_editor(&args);
+ editor = get_editor(&editor_argc, &editor_argv);
/* Install signal handlers to clean up temp files if we are killed. */
setup_signals();
while ((ch = getchar()) != EOF && ch != '\n')
continue;
}
- edit_sudoers(sp, editor, args, -1);
+ edit_sudoers(sp, editor, editor_argc, editor_argv, -1);
}
/*
* Check edited files for a parse error, re-edit any that fail
* and install the edited files as needed.
*/
- if (reparse_sudoers(editor, args, strict, quiet)) {
+ if (reparse_sudoers(editor, editor_argc, editor_argv, strict, quiet)) {
TAILQ_FOREACH(sp, &sudoerslist, entries) {
if (sp->doedit)
(void) install_sudoers(sp, oldperms);
exit(exitcode);
}
+static char *
+get_editor(int *editor_argc, char ***editor_argv)
+{
+ char *editor, *editor_path = NULL, **whitelist = NULL;
+ static char *files[] = { "+1", "sudoers" };
+ unsigned int whitelist_len = 0;
+ debug_decl(get_editor, SUDOERS_DEBUG_UTIL)
+
+ /* Build up editor whitelist from def_editor unless env_editor is set. */
+ if (!def_env_editor) {
+ const char *cp, *ep;
+ const char *def_editor_end = def_editor + strlen(def_editor);
+
+ /* Count number of entries in whitelist and split into a list. */
+ for (cp = sudo_strsplit(def_editor, def_editor_end, ":", &ep);
+ cp != NULL; cp = sudo_strsplit(NULL, def_editor_end, ":", &ep)) {
+ whitelist_len++;
+ }
+ whitelist = reallocarray(NULL, whitelist_len + 1, sizeof(char *));
+ if (whitelist == NULL)
+ sudo_fatalx(U_("unable to allocate memory"));
+ whitelist_len = 0;
+ for (cp = sudo_strsplit(def_editor, def_editor_end, ":", &ep);
+ cp != NULL; cp = sudo_strsplit(NULL, def_editor_end, ":", &ep)) {
+ whitelist[whitelist_len] = strndup(cp, (size_t)(ep - cp));
+ if (whitelist[whitelist_len] == NULL)
+ sudo_fatalx(U_("unable to allocate memory"));
+ whitelist_len++;
+ }
+ whitelist[whitelist_len] = NULL;
+ }
+
+ /* First try to use user's VISUAL or EDITOR environment vars. */
+ if ((editor = getenv("VISUAL")) == NULL || *editor == '\0')
+ editor = getenv("EDITOR");
+ if (editor && *editor == '\0')
+ editor = NULL;
+ if (editor != NULL) {
+ editor_path = resolve_editor(editor, strlen(editor), 2, files,
+ editor_argc, editor_argv, whitelist);
+ if (def_env_editor && editor_path == NULL) {
+ /* If we are honoring $EDITOR this is a fatal error. */
+ sudo_fatalx(U_("specified editor (%s) doesn't exist"), editor);
+ }
+ }
+ if (editor_path == NULL) {
+ /* def_editor could be a path, split it up, avoiding strtok() */
+ const char *def_editor_end = def_editor + strlen(def_editor);
+ const char *cp, *ep;
+ for (cp = sudo_strsplit(def_editor, def_editor_end, ":", &ep);
+ cp != NULL; cp = sudo_strsplit(NULL, def_editor_end, ":", &ep)) {
+ editor_path = resolve_editor(cp, (size_t)(ep - cp), 2, files,
+ editor_argc, editor_argv, whitelist);
+ if (editor_path == NULL && errno != ENOENT)
+ debug_return_str(NULL);
+ } while (ep != NULL && editor_path == NULL);
+ }
+ if (editor_path == NULL)
+ sudo_fatalx(U_("no editor found (editor path = %s)"), def_editor);
+
+ if (whitelist != NULL) {
+ while (whitelist_len--)
+ free(whitelist[whitelist_len]);
+ free(whitelist);
+ }
+
+ debug_return_str(editor_path);
+}
+
/*
* List of editors that support the "+lineno" command line syntax.
* If an entry starts with '*' the tail end of the string is matched.
NULL
};
+/*
+ * Check whether or not the specified editor matched lineno_editors[].
+ * Returns true if yes, false if no.
+ */
+static bool
+editor_supports_plus(const char *editor)
+{
+ const char *editor_base = strrchr(editor, '/');
+ const char *cp;
+ char **av;
+ debug_decl(editor_supports_plus, SUDOERS_DEBUG_UTIL)
+
+ if (editor_base != NULL)
+ editor_base++;
+ else
+ editor_base = editor;
+ if (*editor_base == 'r')
+ editor_base++;
+
+ for (av = lineno_editors; (cp = *av) != NULL; av++) {
+ /* We only handle a leading '*' wildcard. */
+ if (*cp == '*') {
+ size_t blen = strlen(editor_base);
+ size_t clen = strlen(++cp);
+ if (blen >= clen) {
+ if (strcmp(cp, editor_base + blen - clen) == 0)
+ break;
+ }
+ } else if (strcmp(cp, editor_base) == 0)
+ break;
+ }
+ debug_return_bool(cp != NULL);
+}
+
/*
* Edit each sudoers file.
* Returns true on success, else false.
*/
static bool
-edit_sudoers(struct sudoersfile *sp, char *editor, char *args, int lineno)
+edit_sudoers(struct sudoersfile *sp, char *editor, int editor_argc,
+ char **editor_argv, int lineno)
{
int tfd; /* sudoers temp file descriptor */
bool modified; /* was the file modified? */
int ac; /* argument count */
- char **av; /* argument vector for run_command */
- char *cp; /* scratch char pointer */
- char buf[PATH_MAX*2]; /* buffer used for copying files */
+ char buf[4096]; /* buffer used for copying files */
char linestr[64]; /* string version of lineno */
struct timespec ts, times[2]; /* time before and after edit */
struct timespec orig_mtim; /* starting mtime of sudoers file */
times[0].tv_nsec = times[1].tv_nsec = orig_mtim.tv_nsec;
(void) utimensat(AT_FDCWD, sp->tpath, times, 0);
- /* Does the editor support +lineno? */
- if (lineno > 0)
- {
- char *editor_base = strrchr(editor, '/');
- if (editor_base != NULL)
- editor_base++;
- else
- editor_base = editor;
- if (*editor_base == 'r')
- editor_base++;
-
- for (av = lineno_editors; (cp = *av) != NULL; av++) {
- /* We only handle a leading '*' wildcard. */
- if (*cp == '*') {
- size_t blen = strlen(editor_base);
- size_t clen = strlen(++cp);
- if (blen >= clen) {
- if (strcmp(cp, editor_base + blen - clen) == 0)
- break;
- }
- } else if (strcmp(cp, editor_base) == 0)
- break;
- }
- /* Disable +lineno if editor doesn't support it. */
- if (cp == NULL)
+ /* Disable +lineno if editor doesn't support it. */
+ if (lineno > 0 && !editor_supports_plus(editor))
lineno = -1;
- }
-
- /* Find the length of the argument vector */
- ac = 3 + (lineno > 0);
- if (args) {
- bool wasblank;
-
- ac++;
- for (wasblank = false, cp = args; *cp; cp++) {
- if (isblank((unsigned char) *cp))
- wasblank = true;
- else if (wasblank) {
- wasblank = false;
- ac++;
- }
- }
- }
- /* Build up argument vector for the command */
- av = reallocarray(NULL, ac, sizeof(char *));
- if (av == NULL)
- sudo_fatalx(U_("unable to allocate memory"));
- if ((av[0] = strrchr(editor, '/')) != NULL)
- av[0]++;
- else
- av[0] = editor;
- ac = 1;
+ /*
+ * We pre-allocated 2 extra spaces for "+n filename" in argv.
+ * Replace those placeholders with the real values.
+ */
+ ac = editor_argc - 2;
if (lineno > 0) {
- (void) snprintf(linestr, sizeof(linestr), "+%d", lineno);
- av[ac++] = linestr;
- }
- if (args) {
- for ((cp = strtok(args, " \t")); cp; (cp = strtok(NULL, " \t")))
- av[ac++] = cp;
+ (void)snprintf(linestr, sizeof(linestr), "+%d", lineno);
+ editor_argv[ac++] = linestr;
}
- av[ac++] = sp->tpath;
- av[ac++] = NULL;
+ editor_argv[ac++] = sp->tpath;
+ editor_argv[ac++] = NULL;
/*
* Do the edit:
goto done;
}
- if (run_command(editor, av) != -1) {
+ if (run_command(editor, editor_argv) != -1) {
if (sudo_gettime_real(×[1]) == -1) {
sudo_warn(U_("unable to read the clock"));
goto done;
* Parse sudoers after editing and re-edit any ones that caused a parse error.
*/
static bool
-reparse_sudoers(char *editor, char *args, bool strict, bool quiet)
+reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
+ bool strict, bool quiet)
{
struct sudoersfile *sp, *last;
FILE *fp;
/* Edit file with the parse error */
TAILQ_FOREACH(sp, &sudoerslist, entries) {
if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {
- edit_sudoers(sp, editor, args, errorlineno);
+ edit_sudoers(sp, editor, editor_argc, editor_argv,
+ errorlineno);
if (errorfile != NULL)
break;
}
printf(_("press return to edit %s: "), sp->path);
while ((ch = getchar()) != EOF && ch != '\n')
continue;
- edit_sudoers(sp, editor, args, errorlineno);
+ edit_sudoers(sp, editor, editor_argc, editor_argv,
+ errorlineno);
}
/* If all sudoers files parsed OK we are done. */
debug_return_ptr(fp);
}
-static char *
-get_editor(char **args)
-{
- char *Editor, *EditorArgs, *EditorPath, *UserEditor, *UserEditorArgs;
- debug_decl(get_editor, SUDOERS_DEBUG_UTIL)
-
- /*
- * Check VISUAL and EDITOR environment variables to see which editor
- * the user wants to use (we may not end up using it though).
- * If the path is not fully-qualified, make it so and check that
- * the specified executable actually exists.
- */
- UserEditorArgs = NULL;
- if ((UserEditor = getenv("VISUAL")) == NULL || *UserEditor == '\0')
- UserEditor = getenv("EDITOR");
- if (UserEditor && *UserEditor == '\0')
- UserEditor = NULL;
- else if (UserEditor) {
- UserEditorArgs = get_args(UserEditor);
- if (find_path(UserEditor, &Editor, NULL, getenv("PATH"), 0) == FOUND) {
- UserEditor = Editor;
- } else {
- if (def_env_editor) {
- /* If we are honoring $EDITOR this is a fatal error. */
- sudo_fatalx(U_("specified editor (%s) doesn't exist"), UserEditor);
- } else {
- /* Otherwise, just ignore $EDITOR. */
- UserEditor = NULL;
- }
- }
- }
-
- /*
- * See if we can use the user's choice of editors either because
- * we allow any $EDITOR or because $EDITOR is in the allowable list.
- */
- Editor = EditorArgs = EditorPath = NULL;
- if (def_env_editor && UserEditor) {
- Editor = UserEditor;
- EditorArgs = UserEditorArgs;
- } else if (UserEditor) {
- struct stat editor_sb;
- struct stat user_editor_sb;
- char *base, *userbase;
-
- if (stat(UserEditor, &user_editor_sb) != 0) {
- /* Should never happen since we already checked above. */
- sudo_fatal(U_("unable to stat editor (%s)"), UserEditor);
- }
- if ((EditorPath = strdup(def_editor)) == NULL)
- sudo_fatalx(U_("unable to allocate memory"));
- Editor = strtok(EditorPath, ":");
- do {
- EditorArgs = get_args(Editor);
- /*
- * Both Editor and UserEditor should be fully qualified but
- * check anyway...
- */
- if ((base = strrchr(Editor, '/')) == NULL)
- continue;
- if ((userbase = strrchr(UserEditor, '/')) == NULL) {
- Editor = NULL;
- break;
- }
- base++, userbase++;
-
- /*
- * We compare the basenames first and then use stat to match
- * for sure.
- */
- if (strcmp(base, userbase) == 0) {
- if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
- && (editor_sb.st_mode & 0000111) &&
- editor_sb.st_dev == user_editor_sb.st_dev &&
- editor_sb.st_ino == user_editor_sb.st_ino)
- break;
- }
- } while ((Editor = strtok(NULL, ":")));
- }
-
- /*
- * Can't use $EDITOR, try each element of def_editor until we
- * find one that exists, is regular, and is executable.
- */
- if (Editor == NULL || *Editor == '\0') {
- free(EditorPath);
- if ((EditorPath = strdup(def_editor)) == NULL)
- sudo_fatalx(U_("unable to allocate memory"));
- Editor = strtok(EditorPath, ":");
- do {
- EditorArgs = get_args(Editor);
- if (sudo_goodpath(Editor, NULL))
- break;
- } while ((Editor = strtok(NULL, ":")));
-
- /* Bleah, none of the editors existed! */
- if (Editor == NULL || *Editor == '\0')
- sudo_fatalx(U_("no editor found (editor path = %s)"), def_editor);
- }
- *args = EditorArgs;
- debug_return_str(Editor);
-}
-
-/*
- * Split out any command line arguments and return them.
- */
-static char *
-get_args(char *cmnd)
-{
- char *args;
- debug_decl(get_args, SUDOERS_DEBUG_UTIL)
-
- args = cmnd;
- while (*args && !isblank((unsigned char) *args))
- args++;
- if (*args) {
- *args++ = '\0';
- while (*args && isblank((unsigned char) *args))
- args++;
- }
- debug_return_str(*args ? args : NULL);
-}
-
/*
* Look up the hostname and set user_host and user_shost.
*/