diff options
Diffstat (limited to '')
-rw-r--r-- | src/sesh.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/sesh.c b/src/sesh.c new file mode 100644 index 0000000..873748e --- /dev/null +++ b/src/sesh.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2008, 2010-2018 Todd C. Miller <Todd.Miller@sudo.ws> + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <time.h> +#include <unistd.h> +#ifdef HAVE_STDBOOL_H +# include <stdbool.h> +#else +# include "compat/stdbool.h" +#endif /* HAVE_STDBOOL_H */ + +#include "sudo_gettext.h" /* must be included before sudo_compat.h */ + +#include "sudo_compat.h" +#include "sudo_fatal.h" +#include "sudo_conf.h" +#include "sudo_debug.h" +#include "sudo_exec.h" +#include "sudo_plugin.h" +#include "sudo_util.h" + +__dso_public int main(int argc, char *argv[], char *envp[]); + +static int sesh_sudoedit(int argc, char *argv[]); + +/* + * Exit codes defined in sudo_exec.h: + * SESH_SUCCESS (0) ... successful operation + * SESH_ERR_FAILURE (1) ... unspecified error + * SESH_ERR_INVALID (30) ... invalid -e arg value + * SESH_ERR_BAD_PATHS (31) ... odd number of paths + * SESH_ERR_NO_FILES (32) ... copy error, no files copied + * SESH_ERR_SOME_FILES (33) ... copy error, no files copied + */ +int +main(int argc, char *argv[], char *envp[]) +{ + int ret; + debug_decl(main, SUDO_DEBUG_MAIN) + + initprogname(argc > 0 ? argv[0] : "sesh"); + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE_NAME, LOCALEDIR); + textdomain(PACKAGE_NAME); + + if (argc < 2) + sudo_fatalx(U_("requires at least one argument")); + + /* Read sudo.conf and initialize the debug subsystem. */ + if (sudo_conf_read(NULL, SUDO_CONF_DEBUG) == -1) + exit(EXIT_FAILURE); + sudo_debug_register(getprogname(), NULL, NULL, + sudo_conf_debug_files(getprogname())); + + if (strcmp(argv[1], "-e") == 0) { + ret = sesh_sudoedit(argc, argv); + } else { + bool login_shell, noexec = false; + char *cp, *cmnd; + int fd = -1; + + /* If the first char of argv[0] is '-', we are running a login shell. */ + login_shell = argv[0][0] == '-'; + + /* If argv[0] ends in -noexec, pass the flag to sudo_execve() */ + if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0]) + noexec = strcmp(cp, "-noexec") == 0; + + /* If argv[1] is --execfd=%d, extract the fd to exec with. */ + if (strncmp(argv[1], "--execfd=", 9) == 0) { + const char *errstr; + + cp = argv[1] + 9; + fd = strtonum(cp, 0, INT_MAX, &errstr); + if (errstr != NULL) + sudo_fatalx(U_("invalid file descriptor number: %s"), cp); + argv++; + argc--; + } + + /* Shift argv and make a copy of the command to execute. */ + argv++; + argc--; + if ((cmnd = strdup(argv[0])) == NULL) + sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); + + /* If invoked as a login shell, modify argv[0] accordingly. */ + if (login_shell) { + if ((cp = strrchr(argv[0], '/')) == NULL) + sudo_fatal(U_("unable to run %s as a login shell"), argv[0]); + *cp = '-'; + argv[0] = cp; + } + sudo_execve(fd, cmnd, argv, envp, noexec); + sudo_warn(U_("unable to execute %s"), cmnd); + ret = SESH_ERR_FAILURE; + } + sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, ret); + _exit(ret); +} + +static int +sesh_sudoedit(int argc, char *argv[]) +{ + int i, oflags_dst, post, ret = SESH_ERR_FAILURE; + int fd_src = -1, fd_dst = -1, follow = 0; + ssize_t nread, nwritten; + struct stat sb; + struct timespec times[2]; + char buf[BUFSIZ]; + debug_decl(sesh_sudoedit, SUDO_DEBUG_EDIT) + + /* Check for -h flag (don't follow links). */ + if (strcmp(argv[2], "-h") == 0) { + argv++; + argc--; + follow = O_NOFOLLOW; + } + + if (argc < 3) + debug_return_int(SESH_ERR_FAILURE); + + /* + * We need to know whether we are performing the copy operation + * before or after the editing. Without this we would not know + * which files are temporary and which are the originals. + * post = 0 ... before + * post = 1 ... after + */ + if (strcmp(argv[2], "0") == 0) + post = 0; + else if (strcmp(argv[2], "1") == 0) + post = 1; + else /* invalid value */ + debug_return_int(SESH_ERR_INVALID); + + /* Align argv & argc to the beggining of the file list. */ + argv += 3; + argc -= 3; + + /* no files specified, nothing to do */ + if (argc == 0) + debug_return_int(SESH_SUCCESS); + /* odd number of paths specified */ + if (argc & 1) + debug_return_int(SESH_ERR_BAD_PATHS); + + /* + * Use O_EXCL if we are not in the post editing stage + * so that it's ensured that the temporary files are + * created by us and that we are not opening any symlinks. + */ + oflags_dst = O_WRONLY|O_TRUNC|O_CREAT|(post ? follow : O_EXCL); + for (i = 0; i < argc - 1; i += 2) { + const char *path_src = argv[i]; + const char *path_dst = argv[i + 1]; + /* + * Try to open the source file for reading. If it + * doesn't exist, that's OK, we'll create an empty + * destination file. + */ + if ((fd_src = open(path_src, O_RDONLY|follow, S_IRUSR|S_IWUSR)) < 0) { + if (errno != ENOENT) { + sudo_warn("%s", path_src); + if (post) { + ret = SESH_ERR_SOME_FILES; + goto nocleanup; + } else + goto cleanup_0; + } + } + + if ((fd_dst = open(path_dst, oflags_dst, post ? + (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) : (S_IRUSR|S_IWUSR))) < 0) { + /* error - cleanup */ + sudo_warn("%s", path_dst); + if (post) { + ret = SESH_ERR_SOME_FILES; + goto nocleanup; + } else + goto cleanup_0; + } + + if (fd_src != -1) { + while ((nread = read(fd_src, buf, sizeof(buf))) > 0) { + if ((nwritten = write(fd_dst, buf, nread)) != nread) { + sudo_warn("%s", path_src); + if (post) { + ret = SESH_ERR_SOME_FILES; + goto nocleanup; + } else + goto cleanup_0; + } + } + } + + if (!post) { + if (fd_src == -1 || fstat(fd_src, &sb) != 0) + memset(&sb, 0, sizeof(sb)); + /* Make mtime on temp file match src. */ + mtim_get(&sb, times[0]); + times[1].tv_sec = times[0].tv_sec; + times[1].tv_nsec = times[0].tv_nsec; + if (futimens(fd_dst, times) == -1) { + if (utimensat(AT_FDCWD, path_dst, times, 0) == -1) + sudo_warn("%s", path_dst); + } + } + close(fd_dst); + fd_dst = -1; + if (fd_src != -1) { + close(fd_src); + fd_src = -1; + } + } + + ret = SESH_SUCCESS; + if (post) { + /* Remove temporary files (post=1) */ + for (i = 0; i < argc - 1; i += 2) + unlink(argv[i]); + } +nocleanup: + if (fd_dst != -1) + close(fd_dst); + if (fd_src != -1) + close(fd_src); + return(ret); +cleanup_0: + /* Remove temporary files (post=0) */ + for (i = 0; i < argc - 1; i += 2) + unlink(argv[i + 1]); + if (fd_dst != -1) + close(fd_dst); + if (fd_src != -1) + close(fd_src); + return(SESH_ERR_NO_FILES); +} |