mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-08-06 18:00:28 +00:00
sanity-check set-id interpreter script paths
The check is fairly stringent; we do it in a constructor, and we make sure that there actually is an open fd higher than 2 that matches the passed path. There is an argument to be made for trapping in the constructor here, and there is also an argument to be made for failing open like we did previously. We pick a middle ground: GetProgramExecutableName returns empty string. Logic being that ensuring the sanity of whatever set-id script mechanism user is running is above our pay grade, but the path TOCTOU between loader and binary is not.
This commit is contained in:
parent
2a11a09d98
commit
ba2b8b8c68
1 changed files with 35 additions and 2 deletions
|
@ -23,6 +23,7 @@
|
|||
#include "libc/cosmo.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/libgen.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/serialize.h"
|
||||
|
@ -32,12 +33,14 @@
|
|||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/ok.h"
|
||||
|
||||
#define CTL_KERN 1
|
||||
#define KERN_PROC 14
|
||||
#define KERN_PROC_PATHNAME_FREEBSD 12
|
||||
#define KERN_PROC_PATHNAME_NETBSD 5
|
||||
#define DEV_FD "/dev/fd/"
|
||||
|
||||
static struct {
|
||||
atomic_uint once;
|
||||
|
@ -47,6 +50,8 @@ static struct {
|
|||
} u;
|
||||
} g_prog;
|
||||
|
||||
static bool g_ape_loaded;
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
@ -104,6 +109,29 @@ static int TryPath(const char *q, int com) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((__constructor__)) static void setugidNameCheck(void) {
|
||||
char *end;
|
||||
unsigned long rc;
|
||||
if (__program_executable_name) {
|
||||
g_ape_loaded = true;
|
||||
if (issetugid()) {
|
||||
/* we are running as a set-id interpreter script. this is highly unusual.
|
||||
it means either someone installed their ape loader set-id, or they are
|
||||
running a system that supports secure set-id interpreter scripts via a
|
||||
/dev/fd/ path. check for the latter and allow that. otherwise, set the
|
||||
__program_executable_name to NULL since there is an unavoidable TOCTOU
|
||||
problem between the kernel and the loader. */
|
||||
if ((!IsNetbsd() && !IsOpenbsd() && !IsXnu()) /* any others? */ ||
|
||||
0 != strncmp(DEV_FD, __program_executable_name, sizeof(DEV_FD) - 1) ||
|
||||
(rc = strtoul(__program_executable_name + sizeof(DEV_FD) - 1, &end,
|
||||
10)) > INT_MAX ||
|
||||
*end || rc < 3 || fcntl((int)rc, F_GETFD) == -1) {
|
||||
__program_executable_name = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void InitProgramExecutableNameImpl(void) {
|
||||
size_t n;
|
||||
ssize_t got;
|
||||
|
@ -133,8 +161,13 @@ static inline void InitProgramExecutableNameImpl(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
// loader passed us a path. it may be relative.
|
||||
if (__program_executable_name) {
|
||||
if (g_ape_loaded) {
|
||||
/* the loader passed us a path. it may be relative. */
|
||||
if (!__program_executable_name) {
|
||||
/* we rejected the loader path because we are running set-id and it
|
||||
looked insecure. use the empty string. */
|
||||
goto UseEmpty;
|
||||
}
|
||||
if (*__program_executable_name == '/') {
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue