This patch makes BB_EXECVP the gateway to the exec syscall
family.

When called, it first looks for a matching applet, and
executes it directly of indirectly by re-executing the
binary. This new behaviour is configurable by the new
FEATURE_FORCE_NOEXEC option.

When FEATURE_FORCE_APPLETS is enabled, BB_EXECVP will
fail when trying to execute things that are not busybox
applets. This allows more control over the executed
processes.

BB_EXECVPE is introduced to support passing seperate
environment to a BB_EXECVP call, and uses the new
copy_terminated_string_array function to backup the
current environ. This mimics the behaviour of execvpe.

Signed-off-by: Nadav Tasher <[email protected]>
---
 Config.in          | 22 +++++++++++
 include/libbb.h    | 19 +++-------
 libbb/executable.c | 92 +++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 115 insertions(+), 18 deletions(-)

diff --git a/Config.in b/Config.in
index dfab102bb..e5d08b33f 100644
--- a/Config.in
+++ b/Config.in
@@ -311,6 +311,28 @@ config FEATURE_PREFER_APPLETS
        problems in chroot jails without mounted /proc and with ps/top
        (command name can be shown as 'exe' for applets started this way).
 
+config FEATURE_FORCE_NOEXEC
+       bool "call applets without exec"
+       default n
+       depends on FEATURE_PREFER_APPLETS
+       help
+       This is an experimental option which allows calling applets directly
+       and bypassing NOEXEC restrictions, instead of exec'ing /proc/self/exe.
+       This reduces the amount of exec syscalls used when running applets,
+       especially in shells.
+
+       This feature extends the "exec prefers applets" feature.
+
+config FEATURE_FORCE_APPLETS
+       bool "only use applets"
+       default n
+       depends on FEATURE_PREFER_APPLETS
+       help
+       This is an experimental option which makes exec calls fail when trying
+       to execute external binaries that are not part of busybox.
+
+       This feature extends the "exec prefers applets" feature.
+
 config BUSYBOX_EXEC_PATH
        string "Path to busybox executable"
        default "/proc/self/exe"
diff --git a/include/libbb.h b/include/libbb.h
index 4d6193795..bb9261626 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1230,21 +1230,14 @@ int file_is_executable(const char *name) FAST_FUNC;
 char *find_executable(const char *filename, const char **PATHp) FAST_FUNC;
 int executable_exists(const char *filename) FAST_FUNC;
 
-/* BB_EXECxx always execs (it's not doing NOFORK/NOEXEC stuff),
- * but it may exec busybox and call applet instead of searching PATH.
+/* used to copy or backup NULL terminated char arrays */
+char** copy_terminated_string_array(char **array) FAST_FUNC;
+
+/* BB_EXECxx are called instead of using execXX directly, to allow
+ * implementation of NOFORK/NOEXEC applet logic if required.
  */
-#if ENABLE_FEATURE_PREFER_APPLETS
 int BB_EXECVP(const char *file, char *const argv[]) FAST_FUNC;
-#define BB_EXECLP(prog,cmd,...) \
-       do { \
-               if (find_applet_by_name(prog) >= 0) \
-                       execlp(bb_busybox_exec_path, cmd, __VA_ARGS__); \
-               execlp(prog, cmd, __VA_ARGS__); \
-       } while (0)
-#else
-#define BB_EXECVP(prog,cmd)     execvp(prog,cmd)
-#define BB_EXECLP(prog,cmd,...) execlp(prog,cmd,__VA_ARGS__)
-#endif
+int BB_EXECVPE(const char *file, char *const argv[], char *const envp[]) 
FAST_FUNC;
 void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
 
 /* xvfork() can't be a _function_, return after vfork in child mangles stack
diff --git a/libbb/executable.c b/libbb/executable.c
index 09bed1eaf..bfd8ce0f7 100644
--- a/libbb/executable.c
+++ b/libbb/executable.c
@@ -7,6 +7,7 @@
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
 #include "libbb.h"
+#include "busybox.h" /* for APPLET_IS_NOEXEC */
 
 /* check if path points to an executable file;
  * return 1 if found;
@@ -78,15 +79,96 @@ int FAST_FUNC executable_exists(const char *name)
        return ret != NULL;
 }
 
-#if ENABLE_FEATURE_PREFER_APPLETS
-/* just like the real execvp, but try to launch an applet named 'file' first */
+char** copy_terminated_string_array(char **array) {
+       size_t array_length = 0, string_length = 0, index = 0;
+       char **output, **temporary;
+
+       /* calculate array size */
+       for (temporary = array; *temporary; ++temporary)
+               ++array_length;
+
+       /* allocate memory for a new array */
+       output = xmalloc(sizeof(char*) * (array_length + 1));
+
+       /* copy all strings */
+       for (index = 0; index < array_length; ++index) {
+               /* calculate length of string */
+               string_length = strlen(array[index]);
+
+               /* allocate memory for the new string */
+               output[index] = xmalloc(string_length + 1);
+
+               /* copy the string */
+               strncpy(output[index], array[index], string_length);
+
+               /* terminate string manually */
+               output[index][string_length] = '\0';
+       }
+
+       /* terminate the array */
+       output[array_length] = NULL;
+
+       return output;
+}
+
+/* just like the real execvp, but we might try to launch an applet named 
'file' first */
 int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
 {
-       if (find_applet_by_name(file) >= 0)
-               execvp(bb_busybox_exec_path, argv);
+#if ENABLE_FEATURE_PREFER_APPLETS
+       int applet = find_applet_by_name(file);
+       if (applet >= 0) {
+               if (ENABLE_FEATURE_FORCE_NOEXEC || APPLET_IS_NOEXEC(applet)) 
+                       run_noexec_applet_and_exit(applet, file, (char **) 
argv);
+               else
+                       execvp(bb_busybox_exec_path, argv);
+       }
+# if ENABLE_FEATURE_FORCE_APPLETS
+       else {
+               /* set errno accordingly */
+               errno = ENOENT;
+               return -1;
+       }
+# endif
+#endif
+
        return execvp(file, argv);
 }
-#endif
+
+int FAST_FUNC BB_EXECVPE(const char *file, char *const argv[], char *const 
envp[])
+{
+       /* for storing BB_EXECVP return value */
+       int return_value = -1;
+
+       /* copy the environ global */
+       char **original_environ = copy_terminated_string_array(environ), 
**temporary = original_environ;
+
+       /* we can now safely use clearenv */
+       clearenv();
+
+       /* envp is NULL terminated, so we add all of its values */
+       while (*envp)
+               putenv(*envp++);
+
+       /* execute using BB_EXECVP and store return value */
+       return_value = BB_EXECVP(file, argv);
+
+       /* we have to clear the environ before restoring the original
+        * environment, otherwise we'll have values from envp and
+        * from the process executed by BB_EXECVP.
+        */
+       clearenv();
+
+       /* we can now restore all values from our original environ copy */
+       while (*temporary) {
+               printf("pppp %s\n", *temporary);
+               putenv(*temporary++);
+       }
+
+       /* free the array */
+       free(original_environ);
+
+       return return_value;
+}
 
 void FAST_FUNC BB_EXECVP_or_die(char **argv)
 {
-- 
2.43.0

_______________________________________________
busybox mailing list
[email protected]
https://lists.busybox.net/mailman/listinfo/busybox

Reply via email to