Skip to content

Commit

Permalink
Filter environment variables
Browse files Browse the repository at this point in the history
Save all environment variables for later use in the application, clear
environment and re-apply only whitelisted variables for the main
firejail process. The whitelisted environment is only used by C
library. Sandboxed tools will get further variables used
internally (FIREJAIL_*).

All variables will be reapplied for the firejailed application.

This also lifts the length restriction for environment variables,
except for the variables used by Firejail itself or the sandboxed
tools.
  • Loading branch information
topimiettinen committed Feb 8, 2021
1 parent deae313 commit 1c7ea15
Show file tree
Hide file tree
Showing 19 changed files with 284 additions and 119 deletions.
18 changes: 10 additions & 8 deletions src/firejail/appimage.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,16 @@ void appimage_set(const char *appimage) {
errExit("Failed to obtain absolute path");

// set environment
if (setenv("APPIMAGE", abspath, 1) < 0)
errExit("setenv");
if (mntdir && setenv("APPDIR", mntdir, 1) < 0)
errExit("setenv");
if (size != 0 && setenv("ARGV0", appimage, 1) < 0)
errExit("setenv");
if (cfg.cwd && setenv("OWD", cfg.cwd, 1) < 0)
errExit("setenv");
env_store_name_val("APPIMAGE", abspath, SETENV);

if (mntdir)
env_store_name_val("APPDIR", mntdir, SETENV);

if (size != 0)
env_store_name_val("ARGV0", appimage, SETENV);

if (cfg.cwd)
env_store_name_val("OWD", cfg.cwd, SETENV);

// build new command line
if (asprintf(&cfg.command_line, "%s/AppRun", mntdir) == -1)
Expand Down
6 changes: 2 additions & 4 deletions src/firejail/checkcfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,8 @@ int checkcfg(int val) {
}

// file copy limit
else if (strncmp(ptr, "file-copy-limit ", 16) == 0) {
if (setenv("FIREJAIL_FILE_COPY_LIMIT", ptr + 16, 1) == -1)
errExit("setenv");
}
else if (strncmp(ptr, "file-copy-limit ", 16) == 0)
env_store_name_val("FIREJAIL_FILE_COPY_LIMIT", ptr + 16, SETENV);

// timeout for join option
else if (strncmp(ptr, "join-timeout ", 13) == 0)
Expand Down
4 changes: 2 additions & 2 deletions src/firejail/chroot.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ void fs_chroot(const char *rootdir) {

// x11
// if users want this mount, they should set FIREJAIL_CHROOT_X11
if (getenv("FIREJAIL_X11") || getenv("FIREJAIL_CHROOT_X11")) {
if (env_get("FIREJAIL_X11") || env_get("FIREJAIL_CHROOT_X11")) {
if (arg_debug)
printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n");
check_subdir(parentfd, "tmp/.X11-unix", 0);
Expand All @@ -194,7 +194,7 @@ void fs_chroot(const char *rootdir) {
check_subdir(parentfd, "run", 1);

// pulseaudio; only support for default directory /run/user/$UID/pulse
if (getenv("FIREJAIL_CHROOT_PULSE")) {
if (env_get("FIREJAIL_CHROOT_PULSE")) {
char *pulse;
if (asprintf(&pulse, "%s/run/user/%d/pulse", cfg.chrootdir, getuid()) == -1)
errExit("asprintf");
Expand Down
30 changes: 11 additions & 19 deletions src/firejail/dbus.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ void dbus_proxy_start(void) {
errExit("close");

if (arg_dbus_user == DBUS_POLICY_FILTER) {
char *user_env = getenv(DBUS_SESSION_BUS_ADDRESS_ENV);
const char *user_env = env_get(DBUS_SESSION_BUS_ADDRESS_ENV);
if (user_env == NULL) {
char *dbus_user_socket = find_user_socket();
write_arg(args_pipe[1], DBUS_SOCKET_PATH_PREFIX "%s",
Expand All @@ -350,7 +350,7 @@ void dbus_proxy_start(void) {
}

if (arg_dbus_system == DBUS_POLICY_FILTER) {
char *system_env = getenv(DBUS_SYSTEM_BUS_ADDRESS_ENV);
const char *system_env = env_get(DBUS_SYSTEM_BUS_ADDRESS_ENV);
if (system_env == NULL) {
write_arg(args_pipe[1],
DBUS_SOCKET_PATH_PREFIX DBUS_SYSTEM_SOCKET);
Expand Down Expand Up @@ -435,8 +435,8 @@ static void socket_overlay(char *socket_path, char *proxy_path) {
close(fd);
}

static char *get_socket_env(const char *name) {
char *value = getenv(name);
static const char *get_socket_env(const char *name) {
const char *value = env_get(name);
if (value == NULL)
return NULL;
if (strncmp(value, DBUS_SOCKET_PATH_PREFIX,
Expand All @@ -446,21 +446,13 @@ static char *get_socket_env(const char *name) {
}

void dbus_set_session_bus_env(void) {
if (setenv(DBUS_SESSION_BUS_ADDRESS_ENV,
DBUS_SOCKET_PATH_PREFIX RUN_DBUS_USER_SOCKET, 1) == -1) {
fprintf(stderr, "Error: cannot modify " DBUS_SESSION_BUS_ADDRESS_ENV
" required by --dbus-user\n");
exit(1);
}
env_store_name_val(DBUS_SESSION_BUS_ADDRESS_ENV,
DBUS_SOCKET_PATH_PREFIX RUN_DBUS_USER_SOCKET, SETENV);
}

void dbus_set_system_bus_env(void) {
if (setenv(DBUS_SYSTEM_BUS_ADDRESS_ENV,
DBUS_SOCKET_PATH_PREFIX RUN_DBUS_SYSTEM_SOCKET, 1) == -1) {
fprintf(stderr, "Error: cannot modify " DBUS_SYSTEM_BUS_ADDRESS_ENV
" required by --dbus-system\n");
exit(1);
}
env_store_name_val(DBUS_SYSTEM_BUS_ADDRESS_ENV,
DBUS_SOCKET_PATH_PREFIX RUN_DBUS_SYSTEM_SOCKET, SETENV);
}

static void disable_socket_dir(void) {
Expand Down Expand Up @@ -506,7 +498,7 @@ void dbus_apply_policy(void) {
errExit("asprintf");
disable_file_or_dir(dbus_user_socket2);

char *user_env = get_socket_env(DBUS_SESSION_BUS_ADDRESS_ENV);
const char *user_env = get_socket_env(DBUS_SESSION_BUS_ADDRESS_ENV);
if (user_env != NULL && strcmp(user_env, dbus_user_socket) != 0 &&
strcmp(user_env, dbus_user_socket2) != 0)
disable_file_or_dir(user_env);
Expand Down Expand Up @@ -535,7 +527,7 @@ void dbus_apply_policy(void) {

disable_file_or_dir(DBUS_SYSTEM_SOCKET);

char *system_env = get_socket_env(DBUS_SYSTEM_BUS_ADDRESS_ENV);
const char *system_env = get_socket_env(DBUS_SYSTEM_BUS_ADDRESS_ENV);
if (system_env != NULL && strcmp(system_env, DBUS_SYSTEM_SOCKET) != 0)
disable_file_or_dir(system_env);

Expand All @@ -561,4 +553,4 @@ void dbus_apply_policy(void) {

fwarning("An abstract unix socket for session D-BUS might still be available. Use --net or remove unix from --protocol set.\n");
}
#endif // HAVE_DBUSPROXY
#endif // HAVE_DBUSPROXY
164 changes: 134 additions & 30 deletions src/firejail/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@

typedef struct env_t {
struct env_t *next;
char *name;
char *value;
const char *name;
const char *value;
ENV_OP op;
} Env;
static Env *envlist = NULL;
Expand Down Expand Up @@ -117,72 +117,62 @@ void env_ibus_load(void) {
// default sandbox env variables
void env_defaults(void) {
// Qt fixes
if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
errExit("setenv");
if (setenv("QML_DISABLE_DISK_CACHE", "1", 1) < 0)
errExit("setenv");
// if (setenv("QTWEBENGINE_DISABLE_SANDBOX", "1", 1) < 0)
// errExit("setenv");
// if (setenv("MOZ_NO_REMOTE, "1", 1) < 0)
// errExit("setenv");
if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
errExit("setenv");
env_store_name_val("QT_X11_NO_MITSHM", "1", SETENV);
env_store_name_val("QML_DISABLE_DISK_CACHE", "1", SETENV);
// env_store_name_val("QTWEBENGINE_DISABLE_SANDBOX", "1", SETENV);
// env_store_name_val("MOZ_NO_REMOTE, "1", SETENV);
env_store_name_val("container", "firejail", SETENV); // LXC sets container=lxc,
if (!cfg.shell)
cfg.shell = guess_shell();
if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0)
errExit("setenv");
if (cfg.shell)
env_store_name_val("SHELL", cfg.shell, SETENV);

// spawn KIO slaves inside the sandbox
if (setenv("KDE_FORK_SLAVES", "1", 1) < 0)
errExit("setenv");
env_store_name_val("KDE_FORK_SLAVES", "1", SETENV);

// set prompt color to green
int set_prompt = 0;
if (checkcfg(CFG_FIREJAIL_PROMPT))
set_prompt = 1;
else { // check FIREJAIL_PROMPT="yes" environment variable
char *prompt = getenv("FIREJAIL_PROMPT");
const char *prompt = env_get("FIREJAIL_PROMPT");
if (prompt && strcmp(prompt, "yes") == 0)
set_prompt = 1;
}

if (set_prompt) {
if (set_prompt)
//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
errExit("setenv");
}
else {
env_store_name_val("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", SETENV);
else
// remove PROMPT_COMMAND
if (setenv("PROMPT_COMMAND", ":", 1) < 0) // unsetenv() will not work here, bash still picks it up from somewhere
errExit("setenv");
}
env_store_name_val("PROMPT_COMMAND", ":", SETENV); // unsetenv() will not work here, bash still picks it up from somewhere

// set the window title
if (!arg_quiet && isatty(STDOUT_FILENO))
printf("\033]0;firejail %s\007", cfg.window_title);

// pass --quiet as an environment variable, in case the command calls further firejailed commands
if (arg_quiet)
setenv("FIREJAIL_QUIET", "yes", 1);
env_store_name_val("FIREJAIL_QUIET", "yes", SETENV);

fflush(0);
}

// parse and store the environment setting
void env_store(const char *str, ENV_OP op) {
EUID_ASSERT();
assert(str);

// some basic checking
if (*str == '\0')
goto errexit;
char *ptr = strchr(str, '=');
if (op == SETENV) {
if (op == SETENV || op == SETENV_ALLOW_EMPTY) {
if (!ptr)
goto errexit;
ptr++;
if (*ptr == '\0')
if (*ptr == '\0' && op != SETENV_ALLOW_EMPTY)
goto errexit;
op = SETENV;
}

// build list entry
Expand Down Expand Up @@ -210,8 +200,45 @@ void env_store(const char *str, ENV_OP op) {
exit(1);
}

void env_store_name_val(const char *name, const char *val, ENV_OP op) {
assert(name);

// some basic checking
if (*name == '\0')
goto errexit;
if (*val == '\0' && op != SETENV_ALLOW_EMPTY)
goto errexit;

if (op == SETENV_ALLOW_EMPTY)
op = SETENV;

// build list entry
Env *env = calloc(1, sizeof(Env));
if (!env)
errExit("calloc");

env->name = strdup(name);
if (env->name == NULL)
errExit("strdup");

if (op == SETENV) {
env->value = strdup(val);
if (env->value == NULL)
errExit("strdup");
}
env->op = op;

// add entry to the list
env_add(env);
return;

errexit:
fprintf(stderr, "Error: invalid --env setting\n");
exit(1);
}

// set env variables in the new sandbox process
void env_apply(void) {
void env_apply_all(void) {
Env *env = envlist;

while (env) {
Expand All @@ -225,3 +252,80 @@ void env_apply(void) {
env = env->next;
}
}

// get env variable
const char *env_get(const char *name) {
Env *env = envlist;
const char *r = NULL;

while (env) {
if (strcmp(env->name, name) == 0) {
if (env->op == SETENV)
r = env->value;
else if (env->op == RMENV)
r = NULL;
}
env = env->next;
}
return r;
}

static const char * const env_whitelist[] = {
"LANG",
"LANGUAGE",
"LC_MESSAGES",
"PATH"
};

static const char * const env_whitelist_sbox[] = {
"FIREJAIL_DEBUG",
"FIREJAIL_FILE_COPY_LIMIT",
"FIREJAIL_PLUGIN",
"FIREJAIL_QUIET",
"FIREJAIL_SECCOMP_ERROR_ACTION",
"FIREJAIL_TEST_ARGUMENTS",
"FIREJAIL_TRACEFILE"
};

static void env_apply_list(const char * const *list, unsigned int num_items) {
Env *env = envlist;

while (env) {
if (env->op == SETENV) {
for (unsigned int i = 0; i < num_items; i++)
if (strcmp(env->name, list[i]) == 0) {
// sanity check for whitelisted environment variables
if (strlen(env->name) + strlen(env->value) >= MAX_ENV_LEN) {
fprintf(stderr, "Error: too long environment variable %s, please use --rmenv\n", env->name);
exit(1);
}

//fprintf(stderr, "whitelisted env var %s=%s\n", env->name, env->value);
if (setenv(env->name, env->value, 1) < 0)
errExit("setenv");
break;
}
} else if (env->op == RMENV)
unsetenv(env->name);

env = env->next;
}
}

// Filter env variables in main firejail process. All variables will
// be reapplied for the sandboxed app by env_apply_all().
void env_apply_whitelist(void) {
int r;

r = clearenv();
if (r != 0)
errExit("clearenv");

env_apply_list(env_whitelist, ARRAY_SIZE(env_whitelist));
}

// Filter env variables for a sbox app
void env_apply_whitelist_sbox(void) {
env_apply_whitelist();
env_apply_list(env_whitelist_sbox, ARRAY_SIZE(env_whitelist_sbox));
}
11 changes: 9 additions & 2 deletions src/firejail/firejail.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
(void) rv;\
} while (0)

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

// main.c
typedef struct bridge_t {
// on the host
Expand Down Expand Up @@ -655,16 +657,21 @@ int check_kernel_procs(void);
void run_no_sandbox(int argc, char **argv) __attribute__((noreturn));

#define MAX_ENVS 256 // some sane maximum number of environment variables
#define MAX_ENV_LEN (PATH_MAX + 32) // FOOBAR=SOME_PATH
#define MAX_ENV_LEN (PATH_MAX + 32) // FOOBAR=SOME_PATH, only applied to Firejail's own sandboxed apps
// env.c
typedef enum {
SETENV = 0,
SETENV_ALLOW_EMPTY,
RMENV
} ENV_OP;

void env_store(const char *str, ENV_OP op);
void env_apply(void);
void env_store_name_val(const char *name, const char *val, ENV_OP op);
void env_apply_all(void);
void env_apply_whitelist(void);
void env_apply_whitelist_sbox(void);
void env_defaults(void);
const char *env_get(const char *name);
void env_ibus_load(void);

// fs_whitelist.c
Expand Down
Loading

0 comments on commit 1c7ea15

Please sign in to comment.