#include <config.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include <compat/stdbool.h>
#endif
#include <string.h>
#ifdef HAVE_WORDEXP_H
# include <wordexp.h>
#endif
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
#include <sudo_compat.h>
#include <sudo_fatal.h>
#include <sudo_util.h>
#include <sudo_queue.h>
#include <sudo_exec.h>
static bool verbose;
sudo_dso_public int main(int argc, char *argv[], char *envp[]);
static bool
report_status(int status, const char *what)
{
bool ret = false;
if (status == -1) {
if (verbose)
printf("%s: OK (%s)\n", getprogname(), what);
return true;
}
if (WIFEXITED(status)) {
int exitval = WEXITSTATUS(status);
if (exitval == 127) {
if (verbose)
printf("%s: OK (%s)\n", getprogname(), what);
ret = true;
} else {
printf("%s: FAIL (%s) [%d]\n", getprogname(), what, exitval);
}
} else if (WIFSIGNALED(status)) {
printf("%s: FAIL (%s) [signal %d]\n", getprogname(), what,
WTERMSIG(status));
} else {
printf("%s: FAIL (%s) [status %d]\n", getprogname(), what, status);
}
return ret;
}
static int
try_execl(void)
{
pid_t child, pid;
int status;
child = fork();
switch (child) {
case -1:
sudo_fatal_nodebug("fork");
case 0:
execl("/bin/true", "true", (char *)0);
_exit(127);
default:
do {
pid = waitpid(child, &status, 0);
} while (pid == -1 && errno == EINTR);
if (pid == -1)
sudo_fatal_nodebug("waitpid");
if (report_status(status, "execl"))
return 0;
return 1;
}
}
static int
try_system(void)
{
int status;
status = system("/bin/true > /dev/null 2>&1");
if (report_status(status, "system"))
return 0;
return 1;
}
#ifdef HAVE_WORDEXP_H
static int
try_wordexp(void)
{
wordexp_t we;
int rc, ret = 1;
rc = wordexp("$(/bin/echo foo)", &we, 0);
switch (rc) {
case -1:
case 127:
#ifdef WRDE_ERRNO
case WRDE_ERRNO:
#endif
if (verbose)
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
ret = 0;
break;
case WRDE_SYNTAX:
if (verbose)
printf("%s: OK (wordexp) [WRDE_SYNTAX]\n", getprogname());
ret = 0;
break;
case WRDE_CMDSUB:
if (verbose)
printf("%s: OK (wordexp) [WRDE_CMDSUB]\n", getprogname());
ret = 0;
break;
case 0:
if (we.we_wordc == 0) {
if (verbose)
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
wordfree(&we);
ret = 0;
break;
}
wordfree(&we);
FALLTHROUGH;
default:
printf("%s: FAIL (wordexp) [%d]\n", getprogname(), rc);
break;
}
return ret;
}
#endif
sudo_noreturn static void
usage(void)
{
fprintf(stderr, "usage: %s [-v] rexec | /path/to/sudo_noexec.so\n",
getprogname());
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[], char *envp[])
{
int ch, errors = 0, ntests = 0;
initprogname(argc > 0 ? argv[0] : "check_noexec");
while ((ch = getopt(argc, argv, "v")) != -1) {
switch (ch) {
case 'v':
verbose = true;
break;
default:
usage();
}
}
if (argc - optind != 1)
usage();
if (strcmp(argv[optind], "rexec") != 0) {
const char *noexec = argv[optind];
argv[optind] = (char *)"rexec";
execve(argv[0], argv, disable_execute(envp, noexec));
sudo_fatalx_nodebug("execve");
}
ntests++;
errors += try_execl();
ntests++;
errors += try_system();
#ifdef HAVE_WORDEXP_H
ntests++;
errors += try_wordexp();
#endif
if (ntests != 0) {
printf("%s: %d tests run, %d errors, %d%% success rate\n",
getprogname(), ntests, errors, (ntests - errors) * 100 / ntests);
}
return errors;
}