2016-11-14 14:21:08 -07:00
|
|
|
/*
|
2019-04-29 07:21:51 -06:00
|
|
|
* SPDX-License-Identifier: ISC
|
|
|
|
*
|
2017-12-03 17:53:40 -07:00
|
|
|
* Copyright (c) 2016 Todd C. Miller <Todd.Miller@sudo.ws>
|
2016-11-14 14:21:08 -07:00
|
|
|
*
|
|
|
|
* 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/wait.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#ifdef HAVE_STDBOOL_H
|
|
|
|
# include <stdbool.h>
|
|
|
|
#else
|
|
|
|
# include "compat/stdbool.h"
|
|
|
|
#endif /* HAVE_STDBOOL_H */
|
2020-05-18 07:59:24 -06:00
|
|
|
#include <string.h>
|
2016-11-14 14:21:08 -07:00
|
|
|
#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_exec.h"
|
|
|
|
|
2020-08-12 09:57:42 -06:00
|
|
|
sudo_dso_public int main(int argc, char *argv[], char *envp[]);
|
2016-11-14 14:21:08 -07:00
|
|
|
|
|
|
|
static bool
|
|
|
|
report_status(int status, const char *what)
|
|
|
|
{
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
/* system() returns -1 for exec failure. */
|
|
|
|
if (status == -1) {
|
|
|
|
printf("%s: OK (%s)\n", getprogname(), what);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check exit value, expecting 127 for failure */
|
|
|
|
if (WIFEXITED(status)) {
|
|
|
|
int exitval = WEXITSTATUS(status);
|
|
|
|
if (exitval == 127) {
|
|
|
|
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 {
|
|
|
|
/* should not happen */
|
|
|
|
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:
|
|
|
|
/* child */
|
|
|
|
/* Try to exec /bin/true, else exit with value 127. */
|
|
|
|
execl("/bin/true", "true", (char *)0);
|
|
|
|
_exit(127);
|
|
|
|
default:
|
|
|
|
/* parent */
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Try to run /bin/true, system() returns 127 on exec failure. */
|
|
|
|
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;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sudo_noexec.so prevents command substitution via the WRDE_NOCMD flag
|
|
|
|
* where possible.
|
|
|
|
*/
|
|
|
|
rc = wordexp("$(/bin/echo foo)", &we, 0);
|
|
|
|
switch (rc) {
|
|
|
|
case -1:
|
2016-11-30 11:05:42 -07:00
|
|
|
/* sudo's wordexp() wrapper returns -1 if RTLD_NEXT is not supported. */
|
|
|
|
case 127:
|
|
|
|
/* Solaris 10 wordexp() returns 127 for execve() failure. */
|
2016-11-14 14:21:08 -07:00
|
|
|
#ifdef WRDE_ERRNO
|
|
|
|
case WRDE_ERRNO:
|
2016-11-30 11:05:42 -07:00
|
|
|
/* Solaris 11 wordexp() returns WRDE_ERRNO for execve() failure. */
|
|
|
|
#endif
|
|
|
|
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
|
2016-11-14 14:21:08 -07:00
|
|
|
ret = 0;
|
|
|
|
break;
|
2018-06-05 15:37:16 -06:00
|
|
|
case WRDE_SYNTAX:
|
|
|
|
/* FreeBSD returns WRDE_SYNTAX if it can't write to the shell process */
|
|
|
|
printf("%s: OK (wordexp) [WRDE_SYNTAX]\n", getprogname());
|
|
|
|
ret = 0;
|
|
|
|
break;
|
2016-11-14 14:21:08 -07:00
|
|
|
case WRDE_CMDSUB:
|
2016-11-30 11:05:42 -07:00
|
|
|
printf("%s: OK (wordexp) [WRDE_CMDSUB]\n", getprogname());
|
2016-11-14 14:21:08 -07:00
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
/*
|
|
|
|
* On HP-UX 11.00 we don't seem to be able to add WRDE_NOCMD
|
|
|
|
* but the execve() wrapper prevents the command substitution.
|
|
|
|
*/
|
|
|
|
if (we.we_wordc == 0) {
|
2016-11-30 11:05:42 -07:00
|
|
|
printf("%s: OK (wordexp) [%d]\n", getprogname(), rc);
|
|
|
|
wordfree(&we);
|
2016-11-14 14:21:08 -07:00
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wordfree(&we);
|
2020-08-01 13:10:50 -06:00
|
|
|
FALLTHROUGH;
|
2016-11-14 14:21:08 -07:00
|
|
|
default:
|
|
|
|
printf("%s: FAIL (wordexp) [%d]\n", getprogname(), rc);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char *argv[], char *envp[])
|
|
|
|
{
|
|
|
|
int errors = 0;
|
|
|
|
|
|
|
|
initprogname(argc > 0 ? argv[0] : "check_noexec");
|
|
|
|
|
|
|
|
if (argc != 2) {
|
|
|
|
fprintf(stderr, "usage: %s regress | /path/to/sudo_noexec.so\n", getprogname());
|
2020-02-08 12:43:00 -07:00
|
|
|
exit(EXIT_FAILURE);
|
2016-11-14 14:21:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Disable execution for post-exec and re-exec ourself. */
|
|
|
|
if (strcmp(argv[1], "rexec") != 0) {
|
|
|
|
const char *noexec = argv[1];
|
|
|
|
argv[1] = "rexec";
|
|
|
|
execve(argv[0], argv, disable_execute(envp, noexec));
|
|
|
|
sudo_fatalx_nodebug("execve");
|
|
|
|
}
|
|
|
|
|
|
|
|
errors += try_execl();
|
|
|
|
errors += try_system();
|
|
|
|
#ifdef HAVE_WORDEXP_H
|
|
|
|
errors += try_wordexp();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return errors;
|
|
|
|
}
|