mirror of
https://github.com/yuzu-emu/AppImageKit-checkrt.git
synced 2025-01-03 15:35:34 +00:00
Restore environment from parent for external processes
This commit is contained in:
parent
5f97d55211
commit
6059df77c3
28
Makefile
28
Makefile
|
@ -2,20 +2,37 @@ CFLAGS ?= -O2 -Wall -Wextra
|
||||||
LDFLAGS += -s
|
LDFLAGS += -s
|
||||||
BIN = AppRun_patched
|
BIN = AppRun_patched
|
||||||
LIB = exec.so
|
LIB = exec.so
|
||||||
|
EXEC_TEST = exec_test
|
||||||
|
ENV_TEST = env_test
|
||||||
|
|
||||||
|
checkrt: $(BIN) $(LIB)
|
||||||
|
|
||||||
all: $(BIN) $(LIB)
|
test: $(EXEC_TEST) $(ENV_TEST)
|
||||||
|
|
||||||
|
all: checkrt test
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -f $(BIN) $(LIB) *.o AppRun.c AppRun_patched.c
|
-rm -f $(BIN) $(LIB) $(EXEC_TEST) $(ENV_TEST) *.o AppRun.c AppRun_patched.c
|
||||||
|
|
||||||
$(BIN): AppRun_patched.o checkrt.o
|
$(BIN): AppRun_patched.o checkrt.o env.o
|
||||||
|
|
||||||
$(LIB): exec.o
|
$(LIB): exec.o env.o
|
||||||
$(CC) -shared $(LDFLAGS) -o $@ $^ -ldl
|
$(CC) -shared $(LDFLAGS) -o $@ $^ -ldl
|
||||||
|
|
||||||
AppRun_patched.o checkrt.o: CFLAGS += -include checkrt.h
|
AppRun_patched.o checkrt.o: CFLAGS += -include checkrt.h
|
||||||
exec.o: CFLAGS += -fPIC -std=c99
|
exec.o env.o: CFLAGS += -fPIC
|
||||||
|
|
||||||
|
$(EXEC_TEST): CFLAGS += -DEXEC_TEST
|
||||||
|
$(EXEC_TEST): exec.c env.c
|
||||||
|
$(CC) -o $@ $(CFLAGS) $^ -ldl
|
||||||
|
|
||||||
|
$(ENV_TEST): CFLAGS += -DENV_TEST
|
||||||
|
$(ENV_TEST): env.c
|
||||||
|
$(CC) -o $@ $(CFLAGS) $^
|
||||||
|
|
||||||
|
run_tests: $(EXEC_TEST) $(ENV_TEST)
|
||||||
|
./$(ENV_TEST)
|
||||||
|
./$(EXEC_TEST)
|
||||||
|
|
||||||
AppRun_patched.c: AppRun.c
|
AppRun_patched.c: AppRun.c
|
||||||
patch -p1 --output $@ < AppRun.c.patch
|
patch -p1 --output $@ < AppRun.c.patch
|
||||||
|
@ -23,3 +40,4 @@ AppRun_patched.c: AppRun.c
|
||||||
AppRun.c:
|
AppRun.c:
|
||||||
wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/appimagetool/master/src/AppRun.c"
|
wget -c "https://raw.githubusercontent.com/AppImage/AppImageKit/appimagetool/master/src/AppRun.c"
|
||||||
|
|
||||||
|
.PHONY: checkrt test run_tests all clean
|
||||||
|
|
18
README.md
18
README.md
|
@ -16,4 +16,20 @@ You would have to know the library version of the host system and decide whether
|
||||||
application is started. This is exactly what the patched AppRun binary does.
|
application is started. This is exactly what the patched AppRun binary does.
|
||||||
It will search for `usr/optional/libstdc++/libstdc++.so.6` and `usr/optional/libgcc_s/libgcc_s.so.1` inside the AppImage or AppDir.
|
It will search for `usr/optional/libstdc++/libstdc++.so.6` and `usr/optional/libgcc_s/libgcc_s.so.1` inside the AppImage or AppDir.
|
||||||
If found it will compare their internal versions with the ones found on the system and prepend their paths to `LD_LIBRARY_PATH` if necessary.
|
If found it will compare their internal versions with the ones found on the system and prepend their paths to `LD_LIBRARY_PATH` if necessary.
|
||||||
You should also put `exec.so` into `usr/optional`.
|
|
||||||
|
You should also put `exec.so` into `usr/optional`. This exec.so library is intended to restore the environment of the AppImage to its parent.
|
||||||
|
This is done to avoid library clashing of bundled libraries with external processes. e.g when running the web browser
|
||||||
|
|
||||||
|
The intended usage is as follows:
|
||||||
|
|
||||||
|
1. This library is injected to the dynamic loader through LD_PRELOAD
|
||||||
|
automatically in AppRun **only** if `usr/optional/exec.so` exists:
|
||||||
|
e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage`
|
||||||
|
|
||||||
|
2. This library will intercept calls to new processes and will detect whether
|
||||||
|
those calls are for binaries within the AppImage bundle or external ones.
|
||||||
|
|
||||||
|
3. In case it's an internal process, it will not change anything.
|
||||||
|
In case it's an external process, it will restore the environment of
|
||||||
|
the AppImage parent by reading `/proc/[pid]/environ`.
|
||||||
|
This is the conservative approach taken.
|
||||||
|
|
12
checkrt.c
12
checkrt.c
|
@ -103,12 +103,18 @@ void checkrt(char *usr_in_appdir)
|
||||||
if (gcc_bundle_ver > gcc_sys_ver)
|
if (gcc_bundle_ver > gcc_sys_ver)
|
||||||
bundle_gcc = 1;
|
bundle_gcc = 1;
|
||||||
|
|
||||||
if (bundle_cxx == 1 || bundle_gcc == 1) {
|
char *exec_file = malloc(strlen(usr_in_appdir) + 1 + strlen(EXEC_SO) + 1);
|
||||||
|
sprintf(exec_file, "%s/%s", usr_in_appdir, EXEC_SO);
|
||||||
|
f = fopen(exec_file, "r");
|
||||||
|
if (f) {
|
||||||
char *old_ld_preload = getenv("LD_PRELOAD");
|
char *old_ld_preload = getenv("LD_PRELOAD");
|
||||||
optional_ld_preload = malloc(strlen(EXEC_SO) + (old_ld_preload ? 1+strlen(old_ld_preload) : 0) + 13 + len);
|
optional_ld_preload = malloc(strlen(EXEC_SO) + (old_ld_preload ? 1+strlen(old_ld_preload) : 0) + 13 + len);
|
||||||
sprintf(optional_ld_preload, "LD_PRELOAD=%s/" EXEC_SO "%s%s", usr_in_appdir,
|
sprintf(optional_ld_preload, "LD_PRELOAD=%s%s%s", exec_file,
|
||||||
old_ld_preload ? ":" : "", old_ld_preload ? old_ld_preload : "");
|
old_ld_preload ? ":" : "", old_ld_preload ? old_ld_preload : "");
|
||||||
|
DEBUG("optional_ld_preload: %s\n", optional_ld_preload);
|
||||||
|
fclose(f);
|
||||||
}
|
}
|
||||||
|
free(exec_file);
|
||||||
|
|
||||||
if (bundle_cxx == 1 && bundle_gcc == 0) {
|
if (bundle_cxx == 1 && bundle_gcc == 0) {
|
||||||
optional_ld_library_path = malloc(strlen(CXXDIR) + 3 + len);
|
optional_ld_library_path = malloc(strlen(CXXDIR) + 3 + len);
|
||||||
|
@ -124,6 +130,6 @@ void checkrt(char *usr_in_appdir)
|
||||||
sprintf(optional_ld_library_path, "%s", "");
|
sprintf(optional_ld_library_path, "%s", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("optional_ld_library_path: %s\noptional_ld_preload: %s\n", optional_ld_library_path, optional_ld_preload);
|
DEBUG("optional_ld_library_path: %s\n", optional_ld_library_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
debug.h
1
debug.h
|
@ -2,6 +2,7 @@
|
||||||
#define DEBUG_H
|
#define DEBUG_H
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define DEBUG(...) do { \
|
#define DEBUG(...) do { \
|
||||||
if (getenv("APPIMAGE_CHECKRT_DEBUG")) \
|
if (getenv("APPIMAGE_CHECKRT_DEBUG")) \
|
||||||
|
|
120
env.c
Executable file
120
env.c
Executable file
|
@ -0,0 +1,120 @@
|
||||||
|
/* Copyright (c) 2018 Pablo Marcos Oltra <pablo.marcos.oltra@gmail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "env.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static char** env_allocate(size_t size) {
|
||||||
|
return calloc(size + 1, sizeof(char*));
|
||||||
|
}
|
||||||
|
|
||||||
|
void env_free(char* const *env) {
|
||||||
|
size_t len = 0;
|
||||||
|
while (env[len] != 0) {
|
||||||
|
free(env[len]);
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
free((char**)env);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t get_number_of_variables(FILE *file, char **buffer, size_t *len) {
|
||||||
|
size_t number = 0;
|
||||||
|
|
||||||
|
if (getline(buffer, len, file) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char *ptr = *buffer;
|
||||||
|
while (ptr < *buffer + *len) {
|
||||||
|
size_t var_len = strlen(ptr);
|
||||||
|
ptr += var_len + 1;
|
||||||
|
if (var_len == 0)
|
||||||
|
break;
|
||||||
|
number++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return number != 0 ? (ssize_t)number : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* const* env_from_buffer(FILE *file) {
|
||||||
|
char *buffer = NULL;
|
||||||
|
size_t len = 0;
|
||||||
|
size_t num_vars = get_number_of_variables(file, &buffer, &len);
|
||||||
|
char** env = env_allocate(num_vars);
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
char *ptr = buffer;
|
||||||
|
while (ptr < buffer + len && n < num_vars) {
|
||||||
|
size_t var_len = strlen(ptr);
|
||||||
|
if (var_len == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
env[n] = calloc(sizeof(char*), var_len + 1);
|
||||||
|
strncpy(env[n], ptr, var_len + 1);
|
||||||
|
DEBUG("\tenv var copied: %s\n", env[n]);
|
||||||
|
ptr += var_len + 1;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* const* read_env_from_process(pid_t pid) {
|
||||||
|
char buffer[256] = {0};
|
||||||
|
|
||||||
|
snprintf(buffer, sizeof(buffer), "/proc/%d/environ", pid);
|
||||||
|
DEBUG("Reading env from parent process: %s\n", buffer);
|
||||||
|
FILE *env_file = fopen(buffer, "r");
|
||||||
|
if (!env_file) {
|
||||||
|
DEBUG("Error reading file: %s (%s)\n", buffer, strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* const* env = env_from_buffer(env_file);
|
||||||
|
fclose(env_file);
|
||||||
|
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* const* read_parent_env() {
|
||||||
|
pid_t ppid = getppid();
|
||||||
|
return read_env_from_process(ppid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENV_TEST
|
||||||
|
int main() {
|
||||||
|
putenv("APPIMAGE_CHECKRT_DEBUG=1");
|
||||||
|
DEBUG("ENV TEST\n");
|
||||||
|
char **env = NULL;
|
||||||
|
read_parent_env(&env);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
9
env.h
Executable file
9
env.h
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef ENV_H
|
||||||
|
#define END_H
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
char* const* read_parent_env();
|
||||||
|
void env_free(char* const *env);
|
||||||
|
|
||||||
|
#endif
|
384
exec.c
384
exec.c
|
@ -1,317 +1,135 @@
|
||||||
/*
|
/* Copyright (c) 2018 Pablo Marcos Oltra <pablo.marcos.oltra@gmail.com>
|
||||||
* Copyright (C) 2016 Sven Brauch <mail@svenbrauch.de>
|
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* modify it under the terms of the GNU Library General Public
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
* License as published by the Free Software Foundation; either
|
* in the Software without restriction, including without limitation the rights
|
||||||
* version 2 of the License, or (at your option) any later version.
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
*
|
*
|
||||||
* This library is distributed in the hope that it will be useful,
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* copies or substantial portions of the Software.
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Library General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Library General Public License
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* along with this library; see the file COPYING.LIB. If not, write to
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* Boston, MA 02110-1301, USA.
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/*
|
||||||
This library is intended to be used together with the AppImage distribution mechanism.
|
* This exec.so library is intended to restore the environment of the AppImage's
|
||||||
Place the library somewhere in your AppImage and point LD_PRELOAD to it
|
* parent process. This is done to avoid library clashing of bundled libraries
|
||||||
before launching your application.
|
* with external processes. e.g when running the web browser
|
||||||
|
*
|
||||||
Whenever your application invokes a child process through execv() or execve(),
|
* The intended usage is as follows:
|
||||||
this wrapper will intercept the call and see if the child process lies
|
*
|
||||||
outside of the bundled appdir. If it does, the wrapper will attempt to undo
|
* 1. This library is injected to the dynamic loader through LD_PRELOAD
|
||||||
any changes done to environment variables before launching the process,
|
* automatically in AppRun **only** if `usr/optional/exec.so` exists:
|
||||||
since you probably did not intend to launch it with e.g. the LD_LIBRARY_PATH
|
* e.g `LD_PRELOAD=$APPDIR/usr/optional/exec.so My.AppImage`
|
||||||
you previously set for your application.
|
*
|
||||||
|
* 2. This library will intercept calls to new processes and will detect whether
|
||||||
To perform this operation, you have to set the following environment variables:
|
* those calls are for binaries within the AppImage bundle or external ones.
|
||||||
$APPDIR -- path of the AppDir you are launching your application from. If this
|
*
|
||||||
is not present, the wrapper will do nothing.
|
* 3. In case it's an internal process, it will not change anything.
|
||||||
|
* In case it's an external process, it will restore the environment of
|
||||||
For each environment variable you want restored, where {VAR} is the name of the environment
|
* the AppImage parent by reading `/proc/[pid]/environ`.
|
||||||
variable (e.g. "PATH"):
|
* This is the conservative approach taken.
|
||||||
$APPIMAGE_ORIGINAL_{VAR} -- original value of the environment variable
|
*/
|
||||||
$APPIMAGE_STARTUP_{VAR} -- value of the variable when you were starting up
|
|
||||||
your application
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include "env.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <malloc.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef ssize_t (*execve_func_t)(const char* filename, char* const argv[], char* const envp[]);
|
typedef int (*execve_func_t)(const char *filename, char *const argv[], char *const envp[]);
|
||||||
static execve_func_t old_execve = NULL;
|
|
||||||
|
|
||||||
typedef ssize_t (*execvp_func_t)(const char* filename, char* const argv[]);
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||||
//static execvp_func_t old_execvp = NULL;
|
|
||||||
|
|
||||||
// TODO implement me: execl, execlp, execle; but it's annoying work and nothing seems to use them
|
static const char* get_fullpath(const char *filename) {
|
||||||
// typedef int (*execl_func_t)(const char *path, const char *arg);
|
// Try to get the canonical path in case it's a relative path or symbolic
|
||||||
// static execl_func_t old_execl = NULL;
|
// link. Otherwise, use which to get the fullpath of the binary
|
||||||
//
|
char *fullpath = canonicalize_file_name(filename);
|
||||||
// typedef int (*execlp_func_t)(const char *file, const char *arg);
|
DEBUG("filename %s, canonical path %s\n", filename, fullpath);
|
||||||
// static execlp_func_t old_execlp = NULL;
|
if (fullpath)
|
||||||
//
|
return fullpath;
|
||||||
// typedef int (*execle_func_t)(const char *path, const char *arg, char * const envp[]);
|
|
||||||
// static execle_func_t old_execle = NULL;
|
|
||||||
|
|
||||||
typedef int (*execv_func_t)(const char *path, char *const argv[]);
|
return filename;
|
||||||
//static execv_func_t old_execv = NULL;
|
|
||||||
|
|
||||||
typedef int (*execvpe_func_t)(const char *file, char *const argv[], char *const envp[]);
|
|
||||||
static execvpe_func_t old_execvpe = NULL;
|
|
||||||
|
|
||||||
char* APPIMAGE_ORIG_PREFIX = "APPIMAGE_ORIGINAL_";
|
|
||||||
char* APPIMAGE_STARTUP_PREFIX = "APPIMAGE_STARTUP_";
|
|
||||||
char* APPDIR = "APPDIR";
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char** names;
|
|
||||||
char** values;
|
|
||||||
} environment;
|
|
||||||
|
|
||||||
environment environment_alloc(size_t envc) {
|
|
||||||
environment env;
|
|
||||||
env.names = calloc(envc+1, sizeof(char*));
|
|
||||||
env.values = calloc(envc+1, sizeof(char*));
|
|
||||||
return env;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int arr_len(char* const x[]) {
|
static int is_external_process(const char *filename) {
|
||||||
int len = 0;
|
const char *appdir = getenv("APPDIR");
|
||||||
while ( x[len] != 0 ) {
|
if (!appdir)
|
||||||
len++;
|
return 0;
|
||||||
|
DEBUG("APPDIR = %s\n", appdir);
|
||||||
|
|
||||||
|
return strncmp(filename, appdir, MIN(strlen(filename), strlen(appdir)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exec_common(execve_func_t function, const char *filename, char* const argv[], char* const envp[]) {
|
||||||
|
const char *fullpath = get_fullpath(filename);
|
||||||
|
DEBUG("filename %s, fullpath %s\n", filename, fullpath);
|
||||||
|
char* const *env = envp;
|
||||||
|
if (is_external_process(fullpath)) {
|
||||||
|
DEBUG("External process detected. Restoring env vars from parent %d\n", getppid());
|
||||||
|
env = read_parent_env();
|
||||||
|
if (!env)
|
||||||
|
env = envp;
|
||||||
|
else
|
||||||
|
DEBUG("Error restoring env vars from parent\n");
|
||||||
}
|
}
|
||||||
return len;
|
int ret = function(filename, argv, env);
|
||||||
}
|
|
||||||
|
|
||||||
void stringlist_free(char* const envp[]) {
|
if (fullpath != filename)
|
||||||
if ( envp ) {
|
free((char*)fullpath);
|
||||||
for ( int i = 0; i < arr_len(envp); i++ ) {
|
if (env != envp)
|
||||||
free(envp[i]);
|
env_free(env);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char** stringlist_alloc(int size) {
|
|
||||||
char** ret = calloc(size, sizeof(char*));
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int environment_len(const environment env) {
|
int execve(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
return arr_len(env.names);
|
DEBUG("execve call hijacked: %s\n", filename);
|
||||||
|
execve_func_t execve_orig = dlsym(RTLD_NEXT, "execve");
|
||||||
|
if (!execve_orig)
|
||||||
|
DEBUG("Error getting execve original symbol: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
return exec_common(execve_orig, filename, argv, envp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void environment_free(environment env) {
|
int execv(const char *filename, char *const argv[]) {
|
||||||
stringlist_free(env.names);
|
DEBUG("execv call hijacked: %s\n", filename);
|
||||||
stringlist_free(env.values);
|
return execve(filename, argv, environ);
|
||||||
}
|
}
|
||||||
|
|
||||||
void environment_append_item(environment env, char* name, int name_size, char* val, int val_size) {
|
int execvpe(const char *filename, char *const argv[], char *const envp[]) {
|
||||||
int count = environment_len(env);
|
DEBUG("execvpe call hijacked: %s\n", filename);
|
||||||
env.names[count] = calloc(name_size+1, sizeof(char));
|
execve_func_t execve_orig = dlsym(RTLD_NEXT, "execvpe");
|
||||||
env.values[count] = calloc(val_size+1, sizeof(char));
|
if (!execve_orig)
|
||||||
strncpy(env.names[count], name, name_size);
|
DEBUG("Error getting execvpe original symbol: %s\n", strerror(errno));
|
||||||
strncpy(env.values[count], val, val_size);
|
|
||||||
|
return exec_common(execve_orig, filename, argv, envp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int environment_find_name(environment env, char* name, int name_size) {
|
int execvp(const char *filename, char *const argv[]) {
|
||||||
int count = environment_len(env);
|
DEBUG("execvp call hijacked: %s\n", filename);
|
||||||
for ( int i = 0; i < count; i++ ) {
|
return execvpe(filename, argv, environ);
|
||||||
if ( !strncmp(env.names[i], name, name_size) ) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char** environment_to_stringlist(environment env) {
|
#ifdef EXEC_TEST
|
||||||
int len = environment_len(env);
|
int main(int argc, char *argv[]) {
|
||||||
char** ret = stringlist_alloc(len+1);
|
putenv("APPIMAGE_CHECKRT_DEBUG=1");
|
||||||
for ( int i = 0; i < len; i++ ) {
|
DEBUG("EXEC TEST\n");
|
||||||
char* name = env.names[i];
|
execv("./env_test", argv);
|
||||||
char* value = env.values[i];
|
|
||||||
int result_len = strlen(name) + strlen(value) + 1;
|
return 0;
|
||||||
ret[i] = calloc(result_len+1, sizeof(char));
|
|
||||||
strcat(ret[i], name);
|
|
||||||
strcat(ret[i], "=");
|
|
||||||
strcat(ret[i], value);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
char** adjusted_environment(const char* filename, char* const envp[]) {
|
|
||||||
if ( !envp ) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int envc = arr_len(envp);
|
|
||||||
|
|
||||||
char* appdir = NULL;
|
|
||||||
|
|
||||||
environment orig = environment_alloc(envc);
|
|
||||||
environment startup = environment_alloc(envc);
|
|
||||||
int orig_prefix_len = strlen(APPIMAGE_ORIG_PREFIX);
|
|
||||||
int startup_prefix_len = strlen(APPIMAGE_STARTUP_PREFIX);
|
|
||||||
for ( int i = 0; i < envc; i++ ) {
|
|
||||||
char* line = envp[i];
|
|
||||||
int name_size = strchr(line, '=')-line;
|
|
||||||
int val_size = strlen(line)-name_size-1;
|
|
||||||
|
|
||||||
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, orig_prefix_len) ) {
|
|
||||||
environment_append_item(orig, line+orig_prefix_len, name_size-orig_prefix_len,
|
|
||||||
line+name_size+1, val_size);
|
|
||||||
}
|
|
||||||
if ( !strncmp(line, APPIMAGE_STARTUP_PREFIX, startup_prefix_len) ) {
|
|
||||||
environment_append_item(startup, line+startup_prefix_len, name_size-startup_prefix_len,
|
|
||||||
line+name_size+1, val_size);
|
|
||||||
}
|
|
||||||
if ( !strncmp(line, APPDIR, strlen(APPDIR)) ) {
|
|
||||||
appdir = calloc(val_size+1, sizeof(char));
|
|
||||||
strncpy(appdir, line+name_size+1, val_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
environment new_env = environment_alloc(envc);
|
|
||||||
if ( appdir && strncmp(filename, appdir, strlen(appdir)) ) {
|
|
||||||
// we have a value for $APPDIR and are leaving it -- perform replacement
|
|
||||||
for ( int i = 0; i < envc; i++ ) {
|
|
||||||
char* line = envp[i];
|
|
||||||
if ( !strncmp(line, APPIMAGE_ORIG_PREFIX, strlen(APPIMAGE_ORIG_PREFIX)) ||
|
|
||||||
!strncmp(line, APPIMAGE_STARTUP_PREFIX, strlen(APPIMAGE_STARTUP_PREFIX)) )
|
|
||||||
{
|
|
||||||
// we are not interested in the backup vars here, don't copy them over
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int name_size = strchr(line, '=')-line;
|
|
||||||
int val_size = strlen(line)-name_size-1;
|
|
||||||
char* value = line+name_size+1;
|
|
||||||
int value_len = strlen(value);
|
|
||||||
|
|
||||||
int at_startup = environment_find_name(startup, line, name_size);
|
|
||||||
int at_original = environment_find_name(orig, line, name_size);
|
|
||||||
if ( at_startup == -1 || at_original == -1 ) {
|
|
||||||
// no information, just keep it
|
|
||||||
environment_append_item(new_env, line, name_size, value, value_len);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* at_start = startup.values[at_startup];
|
|
||||||
int at_start_len = strlen(at_start);
|
|
||||||
char* at_orig = orig.values[at_original];
|
|
||||||
int at_orig_len = strlen(at_orig);
|
|
||||||
|
|
||||||
// TODO HACK: do not copy over empty vars
|
|
||||||
if ( strlen(at_orig) == 0 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !strncmp(line+name_size+1, startup.values[at_startup], val_size) ) {
|
|
||||||
// nothing changed since startup, restore old value
|
|
||||||
environment_append_item(new_env, line, name_size, at_orig, at_orig_len);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int chars_added = value_len > at_start_len;
|
|
||||||
char* use_value = NULL;
|
|
||||||
if ( chars_added > 0 ) {
|
|
||||||
// something was added to the current value
|
|
||||||
// take _original_ value of the env var and append/prepend the same thing
|
|
||||||
use_value = calloc(strlen(at_orig) + chars_added + 1, sizeof(char));
|
|
||||||
if ( !strncmp(value, at_start, at_start_len) ) {
|
|
||||||
// append case
|
|
||||||
strcat(use_value, value);
|
|
||||||
strcat(use_value, at_orig + strlen(value));
|
|
||||||
}
|
|
||||||
else if ( !strncmp(value+(value_len-at_start_len), at_start, at_start_len) ) {
|
|
||||||
// prepend case
|
|
||||||
strcat(use_value, at_orig + strlen(value));
|
|
||||||
strcat(use_value, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// none of the above methods matched
|
|
||||||
// assume the value changed completely and simply keep what the application set
|
|
||||||
free(use_value);
|
|
||||||
use_value = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !use_value ) {
|
|
||||||
environment_append_item(new_env, line, name_size, value, value_len);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
environment_append_item(new_env, line, name_size, use_value, strlen(use_value));
|
|
||||||
free(use_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char** ret = NULL;
|
|
||||||
if ( environment_len(new_env) > 0 ) {
|
|
||||||
ret = environment_to_stringlist(new_env);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// nothing changed
|
|
||||||
ret = stringlist_alloc(envc+1);
|
|
||||||
for ( int i = 0; i < envc; i++ ) {
|
|
||||||
int len = strlen(envp[i]);
|
|
||||||
ret[i] = calloc(len+1, sizeof(char));
|
|
||||||
strncpy(ret[i], envp[i], len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
environment_free(orig);
|
|
||||||
environment_free(startup);
|
|
||||||
environment_free(new_env);
|
|
||||||
free(appdir);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execve(const char* filename, char* const argv[], char* const envp[]) {
|
|
||||||
char** new_envp = adjusted_environment(filename, envp);
|
|
||||||
old_execve = dlsym(RTLD_NEXT, "execve");
|
|
||||||
int ret = old_execve(filename, argv, new_envp);
|
|
||||||
stringlist_free(new_envp);
|
|
||||||
DEBUG("custom execve()!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execv(const char* filename, char* const argv[]) {
|
|
||||||
char** new_envp = adjusted_environment(filename, environ);
|
|
||||||
old_execve = dlsym(RTLD_NEXT, "execve");
|
|
||||||
int ret = old_execve(filename, argv, new_envp);
|
|
||||||
stringlist_free(new_envp);
|
|
||||||
DEBUG("custom execv()!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execvpe(const char* filename, char* const argv[], char* const envp[]) {
|
|
||||||
// TODO: might not be full path
|
|
||||||
char** new_envp = adjusted_environment(filename, envp);
|
|
||||||
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
|
||||||
int ret = old_execvpe(filename, argv, new_envp);
|
|
||||||
stringlist_free(new_envp);
|
|
||||||
DEBUG("custom execvpe()!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int execvp(const char* filename, char* const argv[]) {
|
|
||||||
// TODO: might not be full path
|
|
||||||
char** new_envp = adjusted_environment(filename, environ);
|
|
||||||
old_execvpe = dlsym(RTLD_NEXT, "execvpe");
|
|
||||||
int ret = old_execvpe(filename, argv, new_envp);
|
|
||||||
stringlist_free(new_envp);
|
|
||||||
DEBUG("custom execvp()!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue