#include <config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif
#ifdef TIME_WITH_SYS_TIME
# include <time.h>
#endif
#include "sudo_gettext.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[]);
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"));
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;
login_shell = argv[0][0] == '-';
if ((cp = strrchr(argv[0], '-')) != NULL && cp != argv[0])
noexec = strcmp(cp, "-noexec") == 0;
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--;
}
argv++;
argc--;
if ((cmnd = strdup(argv[0])) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
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)
if (strcmp(argv[2], "-h") == 0) {
argv++;
argc--;
follow = O_NOFOLLOW;
}
if (argc < 3)
debug_return_int(SESH_ERR_FAILURE);
if (strcmp(argv[2], "0") == 0)
post = 0;
else if (strcmp(argv[2], "1") == 0)
post = 1;
else
debug_return_int(SESH_ERR_INVALID);
argv += 3;
argc -= 3;
if (argc == 0)
debug_return_int(SESH_SUCCESS);
if (argc & 1)
debug_return_int(SESH_ERR_BAD_PATHS);
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];
if ((fd_src = open(path_src, O_RDONLY|follow, 0600)) < 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 ? 0644 : 0600)) < 0) {
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 (fd_dst != -1) {
if (!post) {
if (fd_src == -1 || fstat(fd_src, &sb) != 0)
memset(&sb, 0, sizeof(sb));
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);
}
if (fd_src != -1)
close(fd_src);
fd_dst = fd_src = -1;
}
ret = SESH_SUCCESS;
if (post) {
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:
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);
}