This patch aims to provide suitable proxy functions to the
exec syscall family.

BB_EXECVE and BB_EXECVPE were introduced, acting as a proxies to
the libc execve and execvpe functions.
When called, they first use BB_APPLET_EXECVE and BB_APPLET_EXECVPE
to look for a matching applet (assuming FEATURE_PREFER_APPLETS is
enabled), then tries executing that applet before falling back
to exec calls.

When an applet is found, it is executed in one of two ways:
1. If the applet is marked NOEXEC in applets.h, the applet is
executed using run_noexec_applet_and_exit().
This way of execution requires some manual bootstraping, like
duplicating argv and resetting the environment.
2. If the applet is not marked NOEXEC in applets.h, the applet
is executed by re-executing the busybox executable with new
argv and envp.

When FEATURE_FORCE_APPLETS is enabled, BB_EXECVE and BB_EXECVPE will
fail when trying to execute external binaries, and only pre-compiled
applets will be able to run.

This allows for better control over the programs executed by
busybox.

Notes:
1. BB_EXECVP is redefined as a macro to BB_EXECVPE that uses
environ as the default envp argument.
2. BB_APPLET_EXECVE copies argv to a new heap-allocated array because
run_noexec_applet_and_exit() runs applets directly. Said applets
might try to modify argv, as they are allowed to do so.

Signed-off-by: Nadav Tasher <[email protected]>
---
 Config.in          |  10 +++++
 include/libbb.h    |  38 ++++++++++-------
 libbb/executable.c | 104 +++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 128 insertions(+), 24 deletions(-)

diff --git a/Config.in b/Config.in
index dfab102bb..9d40431ee 100644
--- a/Config.in
+++ b/Config.in
@@ -311,6 +311,16 @@ 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_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..3b29a04ef 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -1230,22 +1230,28 @@ 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.
- */
-#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
-void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
+/* BB_EXECV is just a macro to use BB_EXECVE
+ * with a default environ argument. */
+#define BB_EXECV(pathname, argv) BB_EXECVE(pathname, argv, environ)
+
+/* BB_EXECVP is just a macro to use BB_EXECVPE
+ * with a default environ argument. */
+#define BB_EXECVP(file, argv) BB_EXECVPE(file, argv, environ)
+
+/* when FEATURE_PREFER_APPLETS is enabled, these functions act as a way
+ * to "exec" a built-in applet, either by NOEXEC or by re-exec. */
+int BB_APPLET_EXECVE(const char *name, char *const argv[], char *const envp[]) 
FAST_FUNC;
+int BB_APPLET_EXECVPE(const char *name, char *const argv[], char *const 
envp[]) FAST_FUNC;
+
+/* these functions act as proxies to execve and execvpe, allowing for the
+ * use of BB_APPLET_EXECVE and BB_APPLET_EXECVPE if required. */
+int BB_EXECVE(const char *pathname, char *const argv[], char *const envp[]) 
FAST_FUNC;
+int BB_EXECVPE(const char *file, char *const argv[], char *const envp[]) 
FAST_FUNC;
+
+/* BB_EXECVP_or_die is commonly used in functions that must
+ * exit if the execution of the desired program fails. */
+void BB_EXECVP_or_die_msg(char *const argv[], const char *msg) NORETURN 
FAST_FUNC;
+#define BB_EXECVP_or_die(argv) BB_EXECVP_or_die_msg(argv, "can't execute '%s'")
 
 /* xvfork() can't be a _function_, return after vfork in child mangles stack
  * in the parent. It must be a macro. */
diff --git a/libbb/executable.c b/libbb/executable.c
index 09bed1eaf..eec2ef42d 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,20 +79,107 @@ int FAST_FUNC executable_exists(const char *name)
        return ret != NULL;
 }
 
+int FAST_FUNC BB_APPLET_EXECVE(const char *name, char *const argv[], char 
*const envp[])
+{
 #if ENABLE_FEATURE_PREFER_APPLETS
-/* just like the real execvp, but try to launch an applet named 'file' first */
-int FAST_FUNC BB_EXECVP(const char *file, char *const argv[])
+       int applet = find_applet_by_name(name);
+       if (applet >= 0) {
+               if (APPLET_IS_NOEXEC(applet)) {
+                       /* since run_noexec_applet_and_exit takes char **argv,
+                        * we need to copy argv to a new heap-allocated array. 
*/
+                       char **copied_argv, *const *temporary_argv;
+
+                       /* find the end of argv */
+                       for (temporary_argv = argv; *temporary_argv; 
++temporary_argv);
+
+                       /* allocate a new char** */
+                       copied_argv = xmalloc(sizeof(char *) * (temporary_argv 
- argv + 1));
+
+                       /* duplicate all strings */
+                       for (temporary_argv = argv; *temporary_argv; 
++temporary_argv)
+                               copied_argv[temporary_argv - argv] = 
xstrdup(*temporary_argv);
+
+                       /* make sure the copied argv is terminated */
+                       copied_argv[temporary_argv - argv] = NULL;
+
+                       /* if non-default environ was passed, replace environ */
+                       if (envp != environ) {
+                               clearenv();
+
+                               /* envp is NULL terminated. */
+                               while (*envp)
+                                       putenv(*envp++);
+                       }
+
+                       /* this should never return. */
+                       run_noexec_applet_and_exit(applet, name, copied_argv);
+
+                       /* if this is reached, error out */
+                       errno = ENOEXEC;
+                       return -1;
+               } else {
+                       /* applet has to be executed using an exec syscall */
+                       return execve(bb_busybox_exec_path, argv, envp);
+               }
+       }
+
+       /* no matching applet was found */
+       errno = ENOENT;
+       return -1;
+#else
+       /* applets are not prefered */
+       return 0;
+#endif
+}
+
+int FAST_FUNC BB_APPLET_EXECVPE(const char *name, char *const argv[], char 
*const envp[])
 {
-       if (find_applet_by_name(file) >= 0)
-               execvp(bb_busybox_exec_path, argv);
-       return execvp(file, argv);
+       /* we try calling BB_APPLET_EXECVE with the given name. */
+       int error = BB_APPLET_EXECVE(name, argv, envp);
+
+       /* since this is function is supposed to emulate a PATH
+        * search, we try executing with the basename too. */
+       if (error < 0)
+               error = BB_APPLET_EXECVE(bb_basename(name), argv, envp);
+
+       return error;
 }
+
+/* just like the real execve, but we might try to launch an applet named 
'pathname' first */
+int FAST_FUNC BB_EXECVE(const char *pathname, char *const argv[], char *const 
envp[])
+{
+       if (BB_APPLET_EXECVE(pathname, argv, envp) < 0) {
+#if ENABLE_FEATURE_FORCE_APPLETS
+               /* no external programs are allowed, error out */
+               errno = ENOENT;
+               return -1;
 #endif
+       }
+
+       /* fall back to execvpe */
+       return execve(pathname, argv, envp);
+}
+
 
-void FAST_FUNC BB_EXECVP_or_die(char **argv)
+/* just like the real execvpe, but we might try to launch an applet named 
'file' first */
+int FAST_FUNC BB_EXECVPE(const char *file, char *const argv[], char *const 
envp[])
+{
+       if (BB_APPLET_EXECVPE(file, argv, envp) < 0) {
+#if ENABLE_FEATURE_FORCE_APPLETS
+               /* no external programs are allowed, error out */
+               errno = ENOENT;
+               return -1;
+#endif
+       }
+
+       /* fall back to execvpe */
+       return execvpe(file, argv, envp);
+}
+
+void FAST_FUNC BB_EXECVP_or_die_msg(char *const argv[], const char *msg)
 {
        BB_EXECVP(argv[0], argv);
        /* SUSv3-mandated exit codes */
        xfunc_error_retval = (errno == ENOENT) ? 127 : 126;
-       bb_perror_msg_and_die("can't execute '%s'", argv[0]);
-}
+       bb_perror_msg_and_die(msg, argv[0]);
+}
\ No newline at end of file
-- 
2.43.0

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

Reply via email to