[TACACS+] Add plugin support to bash. (#8660)

This pull request add plugin support library to bash.
    And we will create a TACACS+ plugin for bash in an other PR, which will bring per command authorization feature to bash.

Why I did it
    To support TACACS per command authorization, we check user command before execute it.

How I did it
    Add plugin support to bash.

How to verify it
    UT with CUnit under bash project cover all new code in plugin.c.
    Also pass all current UT.

Which release branch to backport (provide reason below if selected)
    N/A

Description for the changelog
    Add plugin support to bash.
This commit is contained in:
liuh-80 2021-10-11 15:20:51 +08:00 committed by GitHub
parent 32e73b0872
commit 7d40384c58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1388 additions and 9 deletions

View File

@ -265,6 +265,10 @@ sudo rm -rf $FILESYSTEM_ROOT/$SONIC_UTILITIES_PY3_WHEEL_NAME
sudo dpkg --root=$FILESYSTEM_ROOT -i $debs_path/sonic-utilities-data_*.deb || \
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
# Install customized bash version to patch bash plugin support.
sudo dpkg --root=$FILESYSTEM_ROOT -i target/debs/bash_*.deb || \
sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install -f
# sonic-utilities-data installs bash-completion as a dependency. However, it is disabled by default
# in bash.bashrc, so we copy a version of the file with it enabled here.
sudo cp -f $IMAGE_CONFIGS/bash/bash.bashrc $FILESYSTEM_ROOT/etc/

View File

@ -1,14 +1,12 @@
# bash package
#
# Created to patch memory-leak issue in the bash-package included in Debian-8 (Jessie)
# release. This rule file, and the associated building-infra created to solve this
# bug (src/bash/), should be eliminated once the migration to Debian-9 (Stretch) is
# completed.
# Created to patch plugin support in the bash-package included in Debian-11 (Bullseye)
# release.
# Bash major release-number corresponding to Debian-8 (Jessie)
BASH_VERSION_MAJOR = 4.3
# Bash complete release-number. This image contains all 4.3 fixes up to patch '42'.
BASH_VERSION_FULL = $(BASH_VERSION_MAJOR)-14
# Bash major release-number corresponding to Debian-11 (Bullseye)
BASH_VERSION_MAJOR = 5.1
# Bash complete release-number. This image contains all 5.1 fixes up to patch '2'.
BASH_VERSION_FULL = $(BASH_VERSION_MAJOR)-2
export BASH_VERSION_MAJOR BASH_VERSION_FULL

View File

@ -910,7 +910,8 @@ $(addprefix $(TARGET_PATH)/, $(SONIC_INSTALLERS)) : $(TARGET_PATH)/% : \
$(PYTHON_SWSSCOMMON) \
$(PYTHON3_SWSSCOMMON) \
$(SONIC_UTILITIES_DATA) \
$(SONIC_HOST_SERVICES_DATA)) \
$(SONIC_HOST_SERVICES_DATA) \
$(BASH)) \
$$(addprefix $(TARGET_PATH)/,$$($$*_DOCKERS)) \
$$(addprefix $(TARGET_PATH)/,$$(SONIC_PACKAGES_LOCAL)) \
$$(addprefix $(FILES_PATH)/,$$($$*_FILES)) \

View File

@ -250,6 +250,12 @@ RUN apt-get update && apt-get install -y \
iproute2 \
# For bash
texi2html \
sharutils \
locales \
time \
man2html-base \
libcunit1 \
libcunit1-dev \
# For initramfs
shellcheck \
bash-completion \

View File

@ -258,6 +258,12 @@ RUN apt-get update && apt-get install -y \
iproute2 \
# For bash
texi2html \
sharutils \
locales \
time \
man2html-base \
libcunit1 \
libcunit1-dev \
# For initramfs
shellcheck \
bash-completion \

View File

@ -231,6 +231,12 @@ RUN apt-get update && apt-get install -y \
texlive-latex-recommended \
# For bash
texi2html \
sharutils \
locales \
time \
man2html-base \
libcunit1 \
libcunit1-dev \
# For initramfs
bash-completion \
{% if CONFIGURED_ARCH == "amd64" -%}

View File

@ -254,6 +254,12 @@ RUN apt-get update && apt-get install -y \
iproute2 \
# For bash
texi2html \
sharutils \
locales \
time \
man2html-base \
libcunit1 \
libcunit1-dev \
# For initramfs
bash-completion \
{%- if CONFIGURED_ARCH == "amd64" %}

4
src/bash/.gitignore vendored
View File

@ -1,3 +1,7 @@
*
!.gitignore
!Makefile
!Files/
!Files/*
!patches/
!patches/*

View File

@ -0,0 +1,18 @@
#disable some warning because UT need test functions not in header file.
CFLAGS = -Wno-parentheses -Wno-format-security -Wno-implicit-function-declaration -c
IFLAGS = -I.. -I../include -I../lib
MFLAG = -DDEBUG -DBASH_PLUGIN_UT
all:
gcc plugin_test.c $(IFLAGS) $(CFLAGS) -o plugin_test.o
gcc mock_helper.c $(IFLAGS) $(CFLAGS) -o mock_helper.o
gcc ../plugin.c $(IFLAGS) $(CFLAGS) $(MFLAG) -o plugin.o
gcc plugin_test.o mock_helper.o plugin.o -o plugin_test -lc -lcunit
test:
# run unit test, if UT failed, build will break
./plugin_test
clean:
rm *.o
rm plugin_test

View File

@ -0,0 +1,6 @@
# tacacs authorization plugin
plugin=/home/liuh/tacacs-bash-plugin/tacacs-authorization.so
plugin=/usr/lib/bash-plugins/another_test_plugin.so # test comments
# test line

View File

@ -0,0 +1,218 @@
/* mock_helper.c -- mock helper for bash plugin UT. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include "mock_helper.h"
// define BASH_PLUGIN_UT_DEBUG to output UT debug message.
//#define BASH_PLUGIN_UT_DEBUG
#if defined (BASH_PLUGIN_UT_DEBUG)
# define debug_printf printf
#else
# define debug_printf
#endif
/* itrace buffer */
char mock_itrace_message_buffer[1024];
/* bash run command buffer */
char mock_onshell_execve_command_buffer[1024];
/* plugin handles. */
void* mock_plugin_handle = (void*)TEST_MOCK_PLUGIN_HANDLE;
void* mock_plugin_default_function_handle = (void*)0x2234;
void* mock_plugin_on_shell_execve_handle = (void*)0x3234;
char* mock_dlerror_failed = "MOCK error";
char* mock_dlerror = NULL;
/* define test scenarios for mock functions return different value by scenario. */
int test_scenario;
/* define test scenarios for different return value. */
int plugin_init_status;
/* define memory allocate counter. */
int memory_allocate_count;
/* Set test scenario for test*/
void set_test_scenario(int scenario)
{
test_scenario = scenario;
}
/* Get test scenario for test*/
int get_test_scenario()
{
return test_scenario;
}
/* Set plugin init status for test*/
void set_plugin_init_status(int status)
{
plugin_init_status = status;
}
/* Get plugin init status for test*/
int get_plugin_init_status()
{
return plugin_init_status;
}
/* Set memory allocate count for test*/
void set_memory_allocate_count(int count)
{
memory_allocate_count = count;
}
/* Get memory allocate count for test*/
int get_memory_allocate_count()
{
return memory_allocate_count;
}
/* MOCK plugin_init method*/
int mock_plugin_init()
{
set_plugin_init_status(PLUGIN_INITIALIZED);
}
/* MOCK plugin_init method*/
int mock_plugin_uninit()
{
set_plugin_init_status(PLUGIN_NOT_INITIALIZE);
}
/* MOCK on_shell_execve method*/
int mock_on_shell_execve (char *user, int shell_level, char *cmd, char **argv)
{
// set mock command data to buffer for UT.
memset(mock_onshell_execve_command_buffer, 0, sizeof(mock_onshell_execve_command_buffer));
snprintf(mock_onshell_execve_command_buffer, sizeof(mock_onshell_execve_command_buffer), "on_shell_execve: user: %s, level: %d, command: %s, argv: %p\n", user, shell_level, cmd, argv);
debug_printf("MOCK: mock_on_shell_execve: %s\n", mock_onshell_execve_command_buffer);
}
/* MOCK dlopen*/
void *dlopen(const char *filename, int flags)
{
debug_printf("MOCK: dlopen: %s\n", filename);
if (TEST_SCEANRIO_PLUGIN_NOT_EXIT == test_scenario)
{
// return null when plugin not exist
mock_dlerror = mock_dlerror_failed;
return NULL;
}
// all other case return mock handle
mock_dlerror = NULL;
return mock_plugin_handle;
}
/* MOCK dlclose*/
int dlclose(void *handle)
{
debug_printf("MOCK: dlclose: %p\n", handle);
// check if the close handle match the opened handle
CU_ASSERT_EQUAL(handle, mock_plugin_handle);
}
/* MOCK dlsym*/
void *dlsym(void *restrict handle, const char *restrict symbol)
{
debug_printf("MOCK: dlsym: %p, %s\n", handle, symbol);
mock_dlerror = NULL;
switch (test_scenario)
{
case TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT:
if (strcmp(symbol, "on_shell_execve") == 0)
{
mock_dlerror = mock_dlerror_failed;
return NULL;
}
case TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT:
if (strcmp(symbol, "plugin_uninit") == 0)
{
mock_dlerror = mock_dlerror_failed;
return NULL;
}
case TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT:
if (strcmp(symbol, "plugin_init") == 0)
{
mock_dlerror = mock_dlerror_failed;
return NULL;
}
case TEST_SCEANRIO_PLUGIN_INIT_SUCCESS:
if (strcmp(symbol, "plugin_init") == 0)
{
// return mock method handle so plugin framework will call it to initialize
return mock_plugin_init;
}
else if (strcmp(symbol, "plugin_uninit") == 0)
{
// return mock method handle so plugin framework will call it to initialize
return mock_plugin_uninit;
}
else if (strcmp(symbol, "on_shell_execve") == 0)
{
// return mock method handle so plugin framework will call it to initialize
return mock_on_shell_execve;
}
}
return mock_plugin_default_function_handle;
}
/* MOCK dlerror*/
char *dlerror(void)
{
return mock_dlerror;
}
/* MOCK get_string_value*/
char *get_string_value(const char * str)
{
return "1";
}
/* MOCK absolute_program*/
int absolute_program (const char * str)
{
return 0;
}
/* MOCK itrace*/
void itrace (const char * format, ...)
{
// set mock message data to buffer for UT.
memset(mock_itrace_message_buffer, 0, sizeof(mock_itrace_message_buffer));
va_list args;
va_start(args, format);
// save message to buffer to UT check later
vsnprintf(mock_itrace_message_buffer, sizeof(mock_itrace_message_buffer), format, args);
va_end(args);
debug_printf("MOCK: itrace: %s\n", mock_itrace_message_buffer);
}
/* MOCK malloc method*/
void* mock_malloc (size_t size)
{
memory_allocate_count++;
debug_printf("MOCK: malloc memory count: %d\n", memory_allocate_count);
return malloc(size);
}
/* MOCK free method*/
void mock_free (void* ptr)
{
memory_allocate_count--;
debug_printf("MOCK: free memory count: %d\n", memory_allocate_count);
free(ptr);
}

View File

@ -0,0 +1,65 @@
/* plugin.h - functions from plugin.c. */
/* Copyright (C) 1993-2015 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
Bash is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Bash is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Bash. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined (_MOCK_HELPER_H_)
#define _MOCK_HELPER_H_
#include "plugin.h"
#define TEST_MOCK_PLUGIN_HANDLE 0x1234
#define TEST_SCEANRIO_PLUGIN_NOT_EXIT 1
#define TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT 2
#define TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT 3
#define TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT 4
#define TEST_SCEANRIO_PLUGIN_INIT_SUCCESS 5
#define PLUGIN_NOT_INITIALIZE -1
#define PLUGIN_INITIALIZED 1
/* The global plugin list */
extern PLUGIN_NODE *global_plugin_list;
/* itrace buffer */
extern char mock_itrace_message_buffer[1024];
/* bash run command buffer */
extern char mock_onshell_execve_command_buffer[1024];
/* Set test scenario for test*/
void set_test_scenario(int scenario);
/* Get test scenario for test*/
int get_test_scenario();
/* Set plugin init status for test*/
void set_plugin_init_status(int status);
/* Get plugin init status for test*/
int get_plugin_init_status();
/* Set memory allocate count for test*/
void set_memory_allocate_count(int count);
/* Get memory allocate count for test*/
int get_memory_allocate_count();
#endif /* _MOCK_HELPER_H_ */

View File

@ -0,0 +1,217 @@
#include <stdio.h>
#include <string.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include "plugin.h"
#include "mock_helper.h"
int clean_up() {
return 0;
}
int start_up() {
return 0;
}
/* Test plugin not exist scenario */
void testcase_try_load_plugin_by_path_not_exist() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_NOT_EXIT);
try_load_plugin_by_path("./testplugin.so");
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't load plugin ./testplugin.so: MOCK error\n");
}
/* Test plugin exist but not support shell_execve scenario */
void testcase_try_load_plugin_by_path_execve_not_exist() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_EXECVE_NOT_EXIT);
try_load_plugin_by_path("./testplugin.so");
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find on_shell_execve function ./testplugin.so: MOCK error\n");
}
/* Test plugin exist but not support plugin_uninit scenario */
void testcase_try_load_plugin_by_path_plugin_uninit_not_exist() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_UNINIT_NOT_EXIT);
try_load_plugin_by_path("./testplugin.so");
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find plugin_uninit function ./testplugin.so: MOCK error\n");
}
/* Test plugin exist but not support plugin_init scenario */
void testcase_try_load_plugin_by_path_plugin_init_not_exist() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_NOT_EXIT);
try_load_plugin_by_path("./testplugin.so");
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: can't find plugin_init function ./testplugin.so: MOCK error\n");
}
/* Test plugin exist but not support plugin_init scenario */
void testcase_try_load_plugin_by_path_plugin_init_success() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS);
set_memory_allocate_count(0);
set_plugin_init_status(PLUGIN_NOT_INITIALIZE);
try_load_plugin_by_path("./testplugin.so");
// check plugin init success
CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED);
// check API success
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: plugin ./testplugin.so loaded\n");
// check global plugin list not empty and contains correct pluginglobal_plugin_list
CU_ASSERT_NOT_EQUAL(global_plugin_list, NULL);
CU_ASSERT_EQUAL(global_plugin_list->plugin_handle, TEST_MOCK_PLUGIN_HANDLE);
// release all loaded plugins
free_loaded_plugins();
// check if memory fully released
CU_ASSERT_EQUAL(global_plugin_list, NULL);
CU_ASSERT_EQUAL(get_memory_allocate_count(), 0);
}
/* Test free loaded plugins */
void testcase_release_loaded_plugin() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS);
set_memory_allocate_count(0);
try_load_plugin_by_path("./testplugin.so");
// check memory allocated
CU_ASSERT_NOT_EQUAL(get_memory_allocate_count(), 0);
// check plugin init success
CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED);
// release all loaded plugins
free_loaded_plugins();
// check if memory fully released
CU_ASSERT_EQUAL(global_plugin_list, NULL);
CU_ASSERT_EQUAL(get_memory_allocate_count(), 0);
}
/* Test load plugin by config */
void testcase_load_plugin_by_config() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS);
set_memory_allocate_count(0);
load_plugin_by_config("./bash_plugins.conf");
// check memory allocated
CU_ASSERT_NOT_EQUAL(get_memory_allocate_count(), 0);
// check plugin init success
CU_ASSERT_EQUAL(get_plugin_init_status(), PLUGIN_INITIALIZED);
// check target plugin in config file loaded
CU_ASSERT_STRING_EQUAL(mock_itrace_message_buffer, "Plugin: plugin /usr/lib/bash-plugins/another_test_plugin.so loaded\n");
// check there are 2 plugins loaded
CU_ASSERT_EQUAL(get_memory_allocate_count(), 2);
// release all loaded plugins
free_loaded_plugins();
// check if memory fully released
CU_ASSERT_EQUAL(global_plugin_list, NULL);
printf("Count %d\n", get_memory_allocate_count());
CU_ASSERT_EQUAL(get_memory_allocate_count(), 0);
}
/* Test invoke on_shell_execve plugin method */
void testcase_invoke_plugin_on_shell_execve() {
set_test_scenario(TEST_SCEANRIO_PLUGIN_INIT_SUCCESS);
set_memory_allocate_count(0);
load_plugin_by_config("./bash_plugins.conf");
// invoke plugin method
char** pargv = (char**)0x5234;
invoke_plugin_on_shell_execve("testuser", "testcommand", pargv);
printf(mock_onshell_execve_command_buffer);
CU_ASSERT_STRING_EQUAL(mock_onshell_execve_command_buffer, "on_shell_execve: user: testuser, level: 1, command: testcommand, argv: 0x5234\n");
// release all loaded plugins
free_loaded_plugins();
// check if memory fully released
CU_ASSERT_EQUAL(global_plugin_list, NULL);
printf("Count %d\n", get_memory_allocate_count());
CU_ASSERT_EQUAL(get_memory_allocate_count(), 0);
}
int main(void) {
if (CUE_SUCCESS != CU_initialize_registry()) {
return CU_get_error();
}
CU_pSuite ste = CU_add_suite("plugin_test", start_up, clean_up);
if (NULL == ste) {
CU_cleanup_registry();
return CU_get_error();
}
if (CU_get_error() != CUE_SUCCESS) {
fprintf(stderr, "Error creating suite: (%d)%s\n", CU_get_error(), CU_get_error_msg());
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_not_exist()...\n", testcase_try_load_plugin_by_path_not_exist)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_execve_not_exist()...\n", testcase_try_load_plugin_by_path_execve_not_exist)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_uninit_not_exist()...\n", testcase_try_load_plugin_by_path_plugin_uninit_not_exist)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_init_not_exist()...\n", testcase_try_load_plugin_by_path_plugin_init_not_exist)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_try_load_plugin_by_path_plugin_init_success()...\n", testcase_try_load_plugin_by_path_plugin_init_success)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_release_loaded_plugin()...\n", testcase_release_loaded_plugin)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_load_plugin_by_config()...\n", testcase_load_plugin_by_config)) {
CU_cleanup_registry();
return CU_get_error();
}
if (!CU_add_test(ste, "Test testcase_invoke_plugin_on_shell_execve()...\n", testcase_invoke_plugin_on_shell_execve)) {
CU_cleanup_registry();
return CU_get_error();
}
if (CU_get_error() != CUE_SUCCESS) {
fprintf(stderr, "Error adding test: (%d)%s\n", CU_get_error(), CU_get_error_msg());
}
// run all test
CU_basic_set_mode(CU_BRM_VERBOSE);
CU_ErrorCode run_errors = CU_basic_run_suite(ste);
if (run_errors != CUE_SUCCESS) {
fprintf(stderr, "Error running tests: (%d)%s\n", run_errors, CU_get_error_msg());
}
CU_basic_show_failures(CU_get_failure_list());
// use failed UT count as return value
return CU_get_number_of_failure_records();
}

View File

@ -9,8 +9,26 @@ $(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% :
dget -u https://launchpad.net/debian/+archive/primary/+sourcefiles/bash/$(BASH_VERSION_FULL)/bash_$(BASH_VERSION_FULL).dsc
# Apply plugin suport patches
quilt push -a
pushd bash-$(BASH_VERSION_MAJOR)
# build package
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -us -uc -b -j$(SONIC_CONFIG_MAKE_JOBS) --admindir $(SONIC_DPKG_ADMINDIR)
# copy UT code.
cp -a ../Files/. ./
# generate config.h which need by UT
./configure
# generate 'pathnames.h' by make bash again, which is needed by UT.
make
# run UT after config.h ready.
make -C unittest && make test -C unittest
popd
mv $* $(DEST)/

View File

@ -0,0 +1,805 @@
From 79b3c4f7e8589afae4b048d662a56b055436e9ab Mon Sep 17 00:00:00 2001
From: liuh-80 <58683130+liuh-80@users.noreply.github.com>
Date: Fri, 8 Oct 2021 16:36:34 +0800
Subject: [PATCH] Add plugin support to bash.
---
bash-5.1/Makefile.in | 14 +-
bash-5.1/config.h.in | 3 +
bash-5.1/configure | 18 +-
bash-5.1/configure.ac | 10 +
bash-5.1/execute_cmd.c | 16 ++
bash-5.1/plugin.c | 428 +++++++++++++++++++++++++++++++++++++++++
bash-5.1/plugin.h | 79 ++++++++
bash-5.1/shell.c | 12 ++
8 files changed, 571 insertions(+), 9 deletions(-)
create mode 100644 bash-5.1/plugin.c
create mode 100644 bash-5.1/plugin.h
diff --git a/bash-5.1/Makefile.in b/bash-5.1/Makefile.in
index 3e3a5d4..16169cd 100644
--- a/bash-5.1/Makefile.in
+++ b/bash-5.1/Makefile.in
@@ -380,6 +380,9 @@ LTLIBINTL = @LTLIBINTL@
INTLLIBS = @INTLLIBS@
INTLOBJS = @INTLOBJS@
+# Dynamic load library.
+DYNAMICLOAD_LIB = @DYNAMICLOAD_LIB@
+
# Our malloc.
MALLOC_TARGET = @MALLOC_TARGET@
@@ -421,7 +424,7 @@ BASHINCFILES = $(BASHINCDIR)/posixstat.h $(BASHINCDIR)/ansi_stdlib.h \
$(BASHINCDIR)/ocache.h
LIBRARIES = $(GLOB_LIB) $(SHLIB_LIB) $(READLINE_LIB) $(HISTORY_LIB) $(TERMCAP_LIB) \
- $(TILDE_LIB) $(MALLOC_LIB) $(INTL_LIB) $(LIBICONV) $(LOCAL_LIBS)
+ $(TILDE_LIB) $(MALLOC_LIB) $(INTL_LIB) $(LIBICONV) $(LOCAL_LIBS) $(DYNAMICLOAD_LIB)
LIBDEP = $(GLOB_DEP) $(SHLIB_DEP) $(INTL_DEP) $(READLINE_DEP) $(HISTORY_DEP) $(TERMCAP_DEP) \
$(TILDE_DEP) $(MALLOC_DEP)
@@ -441,7 +444,7 @@ CSOURCES = shell.c eval.c parse.y general.c make_cmd.c print_cmd.c y.tab.c \
input.c bashhist.c array.c arrayfunc.c assoc.c sig.c pathexp.c \
unwind_prot.c siglist.c bashline.c bracecomp.c error.c \
list.c stringlib.c locale.c findcmd.c redir.c \
- pcomplete.c pcomplib.c syntax.c xmalloc.c
+ pcomplete.c pcomplib.c syntax.c xmalloc.c plugin.c
HSOURCES = shell.h flags.h trap.h hashcmd.h hashlib.h jobs.h builtins.h \
general.h variables.h config.h $(ALLOC_HEADERS) alias.h \
@@ -449,7 +452,7 @@ HSOURCES = shell.h flags.h trap.h hashcmd.h hashlib.h jobs.h builtins.h \
command.h input.h error.h bashansi.h dispose_cmd.h make_cmd.h \
subst.h externs.h siglist.h bashhist.h bashline.h bashtypes.h \
array.h arrayfunc.h sig.h mailcheck.h bashintl.h bashjmp.h \
- execute_cmd.h parser.h pathexp.h pathnames.h pcomplete.h assoc.h \
+ execute_cmd.h parser.h pathexp.h pathnames.h pcomplete.h assoc.h plugin.h \
$(BASHINCFILES)
SOURCES = $(CSOURCES) $(HSOURCES) $(BUILTIN_DEFS)
@@ -482,7 +485,7 @@ OBJECTS = shell.o eval.o y.tab.o general.o make_cmd.o print_cmd.o $(GLOBO) \
trap.o input.o unwind_prot.o pathexp.o sig.o test.o version.o \
alias.o array.o arrayfunc.o assoc.o braces.o bracecomp.o bashhist.o \
bashline.o $(SIGLIST_O) list.o stringlib.o locale.o findcmd.o redir.o \
- pcomplete.o pcomplib.o syntax.o xmalloc.o $(SIGNAMES_O)
+ pcomplete.o pcomplib.o syntax.o xmalloc.o plugin.o $(SIGNAMES_O)
# Where the source code of the shell builtins resides.
BUILTIN_SRCDIR=$(srcdir)/builtins
@@ -1039,7 +1042,7 @@ eval.o: make_cmd.h subst.h sig.h pathnames.h externs.h parser.h
eval.o: input.h execute_cmd.h
eval.o: bashhist.h assoc.h ${BASHINCDIR}/ocache.h ${BASHINCDIR}/chartypes.h
execute_cmd.o: config.h bashtypes.h ${BASHINCDIR}/filecntl.h ${BASHINCDIR}/posixstat.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h
-execute_cmd.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h
+execute_cmd.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h plugin.h
execute_cmd.o: general.h xmalloc.h bashtypes.h variables.h arrayfunc.h conftypes.h array.h hashlib.h
execute_cmd.o: quit.h ${BASHINCDIR}/maxpath.h unwind_prot.h dispose_cmd.h
execute_cmd.o: make_cmd.h subst.h sig.h pathnames.h externs.h parser.h
@@ -1050,6 +1053,7 @@ execute_cmd.o: ${BASHINCDIR}/posixtime.h ${BASHINCDIR}/chartypes.h
execute_cmd.o: $(DEFSRC)/getopt.h
execute_cmd.o: bashhist.h input.h ${GRAM_H} assoc.h hashcmd.h alias.h
execute_cmd.o: ${BASHINCDIR}/ocache.h ${BASHINCDIR}/posixwait.h
+plugin.o: plugin.h
expr.o: config.h bashansi.h ${BASHINCDIR}/ansi_stdlib.h
expr.o: shell.h syntax.h config.h bashjmp.h ${BASHINCDIR}/posixjmp.h command.h ${BASHINCDIR}/stdc.h error.h
expr.o: general.h xmalloc.h bashtypes.h variables.h arrayfunc.h conftypes.h array.h hashlib.h
diff --git a/bash-5.1/config.h.in b/bash-5.1/config.h.in
index ab316d4..ab5634f 100644
--- a/bash-5.1/config.h.in
+++ b/bash-5.1/config.h.in
@@ -38,6 +38,9 @@
BSD-like job control. */
#undef JOB_CONTROL
+/* Define BASH_SHELL_EXECVE_PLUGIN if need plugin support. */
+#undef BASH_SHELL_EXECVE_PLUGIN
+
/* Define ALIAS if you want the alias features. */
#undef ALIAS
diff --git a/bash-5.1/configure b/bash-5.1/configure
index 0f1d3ed..c462d55 100644
--- a/bash-5.1/configure
+++ b/bash-5.1/configure
@@ -632,6 +632,7 @@ LOCAL_DEFS
LOCAL_LDFLAGS
LOCAL_CFLAGS
LOCAL_LIBS
+DYNAMICLOAD_LIB
MALLOC_DEBUG
DEBUG
RELSTATUS
@@ -858,6 +859,7 @@ enable_single_help_strings
enable_strict_posix_default
enable_usg_echo_default
enable_xpg_echo_default
+enable_bash_shell_execve_plugin
enable_mem_scramble
enable_profiling
enable_static_link
@@ -1568,6 +1570,7 @@ Optional Features:
--enable-xpg-echo-default
make the echo builtin expand escape sequences by
default
+ --enable-bash-plugin enable bash plugin features
--enable-mem-scramble scramble memory on calls to malloc and free
--enable-profiling allow profiling with gprof
--enable-static-link link bash statically, for use as a root shell
@@ -3027,6 +3030,7 @@ opt_dircomplete_expand_default=no
opt_globascii_default=yes
opt_function_import=yes
opt_dev_fd_stat_broken=no
+opt_bash_shell_execve_plugin=yes
opt_static_link=no
opt_profiling=no
@@ -3048,6 +3052,7 @@ if test $opt_minimal_config = yes; then
opt_multibyte=yes opt_cond_regexp=no opt_coproc=no
opt_casemod_attrs=no opt_casemod_expansions=no opt_extglob_default=no
opt_globascii_default=yes
+ opt_bash_shell_execve_plugin=no
fi
# Check whether --enable-alias was given.
@@ -3235,6 +3240,10 @@ if test "${enable_xpg_echo_default+set}" = set; then :
enableval=$enable_xpg_echo_default; opt_xpg_echo=$enableval
fi
+# Check whether --enable-bash-shell-execve-plugin was given.
+if test "${enable_bash_shell_execve_plugin+set}" = set; then :
+ enableval=$enable_bash_shell_execve_plugin; opt_bash_shell_execve_plugin=$enableval
+fi
# Check whether --enable-mem-scramble was given.
if test "${enable_mem_scramble+set}" = set; then :
@@ -3254,10 +3263,11 @@ fi
-
-
-
-
+DYNAMICLOAD_LIB=
+if test $opt_bash_shell_execve_plugin = yes; then
+$as_echo "#define BASH_SHELL_EXECVE_PLUGIN 1" >>confdefs.h
+DYNAMICLOAD_LIB=-ldl
+fi
if test $opt_alias = yes; then
$as_echo "#define ALIAS 1" >>confdefs.h
diff --git a/bash-5.1/configure.ac b/bash-5.1/configure.ac
index 2fe3e7d..0064683 100644
--- a/bash-5.1/configure.ac
+++ b/bash-5.1/configure.ac
@@ -182,6 +182,7 @@ opt_dircomplete_expand_default=no
opt_globascii_default=yes
opt_function_import=yes
opt_dev_fd_stat_broken=no
+opt_bash_shell_execve_plugin=yes
dnl options that affect how bash is compiled and linked
opt_static_link=no
@@ -203,6 +204,7 @@ if test $opt_minimal_config = yes; then
opt_multibyte=yes opt_cond_regexp=no opt_coproc=no
opt_casemod_attrs=no opt_casemod_expansions=no opt_extglob_default=no
opt_globascii_default=yes
+ opt_bash_shell_execve_plugin=no
fi
AC_ARG_ENABLE(alias, AC_HELP_STRING([--enable-alias], [enable shell aliases]), opt_alias=$enableval)
@@ -242,6 +244,7 @@ AC_ARG_ENABLE(single-help-strings, AC_HELP_STRING([--enable-single-help-strings]
AC_ARG_ENABLE(strict-posix-default, AC_HELP_STRING([--enable-strict-posix-default], [configure bash to be posix-conformant by default]), opt_strict_posix=$enableval)
AC_ARG_ENABLE(usg-echo-default, AC_HELP_STRING([--enable-usg-echo-default], [a synonym for --enable-xpg-echo-default]), opt_xpg_echo=$enableval)
AC_ARG_ENABLE(xpg-echo-default, AC_HELP_STRING([--enable-xpg-echo-default], [make the echo builtin expand escape sequences by default]), opt_xpg_echo=$enableval)
+AC_ARG_ENABLE(bash-shell-execve-plugin, AC_HELP_STRING([--enable-bash-shell-execve-plugin], [enable bash shell execve plugin features]), opt_bash_shell_execve_plugin=$enableval)
dnl options that alter how bash is compiled and linked
AC_ARG_ENABLE(mem-scramble, AC_HELP_STRING([--enable-mem-scramble], [scramble memory on calls to malloc and free]), opt_memscramble=$enableval)
@@ -260,6 +263,13 @@ dnl opt_readline and opt_history are handled later, because AC_PROG_CC needs
dnl to be run before we can check the version of an already-installed readline
dnl library
+DYNAMICLOAD_LIB=
+if test $opt_bash_shell_execve_plugin = yes; then
+AC_DEFINE(BASH_SHELL_EXECVE_PLUGIN)
+DYNAMICLOAD_LIB=-ldl
+fi
+AC_SUBST(DYNAMICLOAD_LIB)
+
if test $opt_alias = yes; then
AC_DEFINE(ALIAS)
fi
diff --git a/bash-5.1/execute_cmd.c b/bash-5.1/execute_cmd.c
index d2a0dd7..fb05489 100644
--- a/bash-5.1/execute_cmd.c
+++ b/bash-5.1/execute_cmd.c
@@ -82,6 +82,10 @@ extern int errno;
# include "test.h"
#endif
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+#include "plugin.h"
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
+
#include "builtins/common.h"
#include "builtins/builtext.h" /* list of builtins */
@@ -5592,6 +5596,18 @@ execute_disk_command (words, redirects, command_line, pipe_in, pipe_out,
leave it there, in the same format that the user used to
type it in. */
args = strvec_from_word_list (words, 0, 0, (int *)NULL);
+
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+ result = invoke_plugin_on_shell_execve (current_user.user_name, command, args);
+
+#if defined (DEBUG)
+ itrace("invoke_plugin_on_shell_execve: failed invoke plugin with user:%s, command:%s, result: %d", current_user.user_name, command, result);
+#endif
+ if (result) {
+ exit (EXECUTION_FAILURE);
+ }
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
+
exit (shell_execve (command, args, export_env));
}
else
diff --git a/bash-5.1/plugin.c b/bash-5.1/plugin.c
new file mode 100644
index 0000000..df72830
--- /dev/null
+++ b/bash-5.1/plugin.c
@@ -0,0 +1,428 @@
+/* plugin.c -- Bash plugin support. */
+
+/* Copyright (C) 1987-2016 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#if !defined (__GNUC__) && !defined (HAVE_ALLOCA_H) && defined (_AIX)
+ #pragma alloca
+#endif /* _AIX && RISC6000 && !__GNUC__ */
+
+// disable bash memory management when build for UT, PTR_T defined in xmalloc.h, define here for disable warning
+#if defined (BASH_PLUGIN_UT)
+# define _XMALLOC_H_
+# define PTR_T void *
+# define malloc mock_malloc
+# define free mock_free
+#else
+#endif
+
+#include <stdio.h>
+#include <dlfcn.h>
+#include "chartypes.h"
+#include "bashtypes.h"
+#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
+# include <sys/file.h>
+#endif
+#include "filecntl.h"
+#include "posixstat.h"
+#include <signal.h>
+#include <syslog.h>
+#if defined (HAVE_SYS_PARAM_H)
+# include <sys/param.h>
+#endif
+
+#if defined (HAVE_UNISTD_H)
+# include <unistd.h>
+#endif
+
+#include "posixtime.h"
+
+#if defined (HAVE_SYS_RESOURCE_H) && !defined (RLIMTYPE)
+# include <sys/resource.h>
+#endif
+
+#if defined (HAVE_SYS_TIMES_H) && defined (HAVE_TIMES)
+# include <sys/times.h>
+#endif
+
+#include <errno.h>
+
+#if !defined (errno)
+extern int errno;
+#endif
+
+#define NEED_FPURGE_DECL
+#define NEED_SH_SETLINEBUF_DECL
+
+#include "bashansi.h"
+#include "bashintl.h"
+
+#include "shell.h"
+#include <y.tab.h> /* use <...> so we pick it up from the build directory */
+#include "error.h"
+#include "flags.h"
+#include "builtins.h"
+#include "hashlib.h"
+#include "jobs.h"
+#include "execute_cmd.h"
+#include "findcmd.h"
+#include "redir.h"
+#include "trap.h"
+#include "pathexp.h"
+#include "hashcmd.h"
+
+
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+#include "plugin.h"
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
+
+#if defined (COND_COMMAND)
+# include "test.h"
+#endif
+
+#include "builtins/common.h"
+
+#include "builtins/getopt.h"
+
+#include <glob/strmatch.h>
+#include <tilde/tilde.h>
+
+#if defined (BUFFERED_INPUT)
+# include "input.h"
+#endif
+
+#if defined (ALIAS)
+# include "alias.h"
+#endif
+
+#if defined (HISTORY)
+# include "bashhist.h"
+#endif
+
+#if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
+# include <mbstr.h> /* mbschr */
+#endif
+
+/* configuration buffer size */
+#define CONFIG_BUFFER_SIZE 256
+
+/* configuration plugin setting key */
+#define CONFIG_PLUGIN_SETTING_KEY "plugin="
+
+/* configuration termination characters */
+#define CONFIG_SETTING_TERMINATIONS " \t\n\r\f"
+
+/* configuration comments start character */
+#define CONFIG_SETTING_COMMENTS_START_CHAR '#'
+
+/* plugin configration file */
+const char *plugin_config_file = "/etc/bash_plugins.conf";
+
+/* plugin on_shell_execve function name */
+static const char *on_shell_execve_function_name = "on_shell_execve";
+
+/* plugin plugin_init function name */
+static const char *plugin_init_function_name = "plugin_init";
+
+/* plugin plugin_uninit function name */
+static const char *plugin_uninit_function_name = "plugin_uninit";
+
+/* plugin handle for test */
+PLUGIN_NODE *global_plugin_list = NULL;
+
+/* Load plugin by plugin path */
+int
+append_plugin(
+ plugin_handle,
+ on_shell_execve,
+ plugin_init,
+ plugin_uninit)
+ void *plugin_handle;
+ on_shell_execve_t *on_shell_execve;
+ plugin_init_t *plugin_init;
+ plugin_uninit_t *plugin_uninit;
+{
+ /* Create and initialize new plugin */
+ PLUGIN_NODE *new_plugin_node = (PLUGIN_NODE*)malloc(sizeof(PLUGIN_NODE));
+ if (new_plugin_node == NULL)
+ {
+ /* When allocate memory failed, stop and return. also output log to both syslog and stderr with LOG_PERROR*/
+ syslog(LOG_PERROR, "Plugin: failed to allocate memory for plugin node.\n");
+ return PLUGIN_LOAD_FAILED;
+ }
+
+ new_plugin_node->next = NULL;
+ new_plugin_node->plugin_handle = plugin_handle;
+ new_plugin_node->on_shell_execve = on_shell_execve;
+ new_plugin_node->plugin_init = plugin_init;
+ new_plugin_node->plugin_uninit = plugin_uninit;
+
+#ifdef DEBUG
+ itrace("Plugin: append plugin node %p to global list %p\n", new_plugin_node, global_plugin_list);
+#endif
+
+ /* Find the pointer to the latest plugin node's 'next' field */
+ PLUGIN_NODE **current_plugin_node = &global_plugin_list;
+ while (*current_plugin_node != NULL) {
+ current_plugin_node = &((*current_plugin_node)->next);
+
+#ifdef DEBUG
+ itrace("Plugin: founded next plugin node: %p\n", *current_plugin_node);
+#endif
+ }
+
+ /* append new plugin to tail node */
+ *current_plugin_node = new_plugin_node;
+
+#ifdef DEBUG
+ itrace("Plugin: append new plugin node %p to %p\n", new_plugin_node, current_plugin_node);
+#endif
+
+ return PLUGIN_LOAD_SUCCESS;
+}
+
+
+/* Load plugin by plugin path */
+void
+try_load_plugin_by_path(plugin_path)
+ const char *plugin_path;
+{
+ /* Plugin handle */
+ void *plugin_handle;
+ if ( (plugin_handle = dlopen(plugin_path, RTLD_LAZY)) == NULL) {
+#ifdef DEBUG
+ itrace("Plugin: can't load plugin %s: %s\n", plugin_path, dlerror());
+#endif
+ return;
+ }
+
+ /* Check if plugin support shell execve method */
+ on_shell_execve_t* plugin_on_shell_execve_handle = dlsym(plugin_handle, on_shell_execve_function_name);
+ if (dlerror() != NULL) {
+ dlclose(plugin_handle);
+
+#ifdef DEBUG
+ itrace("Plugin: can't find on_shell_execve function %s: %s\n", plugin_path, dlerror());
+#endif
+ return;
+ }
+
+
+ /* Check if plugin support un-initialization method */
+ plugin_uninit_t* plugin_uninit_handle = dlsym(plugin_handle, plugin_uninit_function_name);
+ if (dlerror() != NULL) {
+ dlclose(plugin_handle);
+
+#ifdef DEBUG
+ itrace("Plugin: can't find plugin_uninit function %s: %s\n", plugin_path, dlerror());
+#endif
+ return;
+ }
+
+ /* Check if plugin support initialization method */
+ plugin_init_t* plugin_init_handle = dlsym(plugin_handle, plugin_init_function_name);
+ if (dlerror() != NULL) {
+ dlclose(plugin_handle);
+
+#ifdef DEBUG
+ itrace("Plugin: can't find plugin_init function %s: %s\n", plugin_path, dlerror());
+#endif
+ return;
+ }
+ else {
+ /* Initialize plugin */
+ plugin_init_handle();
+ }
+
+ /* Add plugin to plugin list */
+ int plugin_load_result = append_plugin(plugin_handle,
+ plugin_on_shell_execve_handle,
+ plugin_init_handle,
+ plugin_uninit_handle);
+
+ if (plugin_load_result == PLUGIN_LOAD_SUCCESS) {
+#ifdef DEBUG
+ itrace("Plugin: plugin %s loaded\n", plugin_path);
+#endif
+ }
+ else {
+ /* Output plugin load error message, also output log to both syslog and stderr with LOG_PERROR*/
+ syslog(LOG_PERROR,"Plugin: plugin %s load failed, result: %d\n", plugin_path, plugin_load_result);
+ }
+}
+
+/* Load plugin by config file */
+void
+load_plugin_by_config(config_filename)
+ const char *config_filename;
+{
+ FILE *config_file;
+ char buffer[CONFIG_BUFFER_SIZE];
+
+ config_file = fopen(config_filename, "r");
+ if(config_file == NULL) {
+#ifdef DEBUG
+ itrace("Plugin: can't open plugin config file %s: %s\n", config_filename, strerror(errno));
+#endif
+ return;
+ }
+
+ while(fgets(buffer, sizeof buffer, config_file)) {
+ if(*buffer == CONFIG_SETTING_COMMENTS_START_CHAR || isspace(*buffer)) {
+ /* ignore comments or white space. */
+ continue;
+ }
+
+ /* read to first whitespace. */
+ strtok(buffer, CONFIG_SETTING_TERMINATIONS);
+
+ if(!strncmp(buffer, CONFIG_PLUGIN_SETTING_KEY, strlen(CONFIG_PLUGIN_SETTING_KEY))) {
+ /* read plugin path. */
+ char* plugin_path = strtok(buffer + strlen(CONFIG_PLUGIN_SETTING_KEY), CONFIG_SETTING_TERMINATIONS);
+#ifdef DEBUG
+ itrace("Plugin: load plugin: %s\n", plugin_path);
+#endif
+ try_load_plugin_by_path(plugin_path);
+ }
+#ifdef DEBUG
+ else {
+ /* output debug message. */
+ itrace("Plugin: unrecognized parameter: %s\n", buffer);
+ }
+#endif
+ }
+
+ fclose(config_file);
+}
+
+/* Free loaded plugins */
+void
+free_loaded_plugins()
+{
+ if (global_plugin_list == NULL) {
+ return;
+ }
+
+#ifdef DEBUG
+ itrace("Plugin: start free plugin from global list %p\n", global_plugin_list);
+#endif
+
+ /* Walk to last plugin */
+ PLUGIN_NODE *next_plugin_node = global_plugin_list;
+ while (next_plugin_node != NULL) {
+
+ /* Unload plugin */
+ next_plugin_node->plugin_uninit();
+ dlclose(next_plugin_node->plugin_handle);
+
+ /* Continue with next pligin */
+ PLUGIN_NODE* current_plugin_node_memory = next_plugin_node;
+ next_plugin_node = next_plugin_node->next;
+
+#ifdef DEBUG
+ itrace("Plugin: next plugin address %p\n", next_plugin_node);
+#endif
+
+ /* Free plugin node memory, this may also reset all allocated memory depends on c lib implementation */
+ free(current_plugin_node_memory);
+ }
+
+ /* Reset plugin list */
+ global_plugin_list = NULL;
+}
+
+/* Invoke loaded plugins */
+int
+invoke_loaded_plugins (user, shell_level, cmd, argv)
+ char *user;
+ int shell_level;
+ char *cmd;
+ char **argv;
+{
+ if (global_plugin_list == NULL) {
+ return 0;
+ }
+
+#ifdef DEBUG
+ itrace("Plugin: start invoke plugin from global list %p\n", global_plugin_list);
+#endif
+
+ /* Walk to last plugin */
+ PLUGIN_NODE *next_plugin_node = global_plugin_list;
+ while (next_plugin_node != NULL) {
+
+ /* Call plugin method */
+ int plugin_error_code = next_plugin_node->on_shell_execve(user, shell_level, cmd, argv);
+ if (plugin_error_code != 0) {
+#ifdef DEBUG
+ itrace("Plugin: on_execve return error: %d\n", plugin_error_code);
+#endif
+ /* Exit when plugin failed */
+ return plugin_error_code;
+ }
+
+ /* Continue with next pligin */
+ next_plugin_node = next_plugin_node->next;
+
+#ifdef DEBUG
+ itrace("Plugin: next plugin address %p\n", next_plugin_node);
+#endif
+ }
+
+ return 0;
+}
+
+/* Load all plugins。 */
+void
+load_plugins ()
+{
+ load_plugin_by_config(plugin_config_file);
+}
+
+/* Free all plugins */
+void
+free_plugins ()
+{
+ free_loaded_plugins();
+}
+
+/* Invoke plugins before shell execve */
+int
+invoke_plugin_on_shell_execve (user, cmd, argv)
+ char *user;
+ char *cmd;
+ char **argv;
+{
+ const char* shell_level_str = get_string_value ("SHLVL");
+ const int shell_level = atoi (shell_level_str);
+
+ if (absolute_program (cmd)) {
+ // find real path for relative path command
+ char resolved_path[PATH_MAX];
+
+ // real_path_buffer should not free here because we pass resolved_path as parameter.
+ char* real_path_buffer = realpath(cmd, resolved_path);
+
+ return invoke_loaded_plugins(user, shell_level, resolved_path, argv);
+ }
+ else {
+ return invoke_loaded_plugins(user, shell_level, cmd, argv);
+ }
+}
diff --git a/bash-5.1/plugin.h b/bash-5.1/plugin.h
new file mode 100644
index 0000000..116b2c5
--- /dev/null
+++ b/bash-5.1/plugin.h
@@ -0,0 +1,79 @@
+/* plugin.h - functions from plugin.c. */
+
+/* Copyright (C) 1993-2015 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Bash is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#if !defined (_PLUGIN_H_)
+#define _PLUGIN_H_
+
+#include "stdc.h"
+
+/* System-wide bash plugin configuration. */
+#define SYS_BASH_PLUGIN "/etc/bash.plugin"
+
+/* Plugin load result. */
+#define PLUGIN_LOAD_SUCCESS 0
+#define PLUGIN_LOAD_FAILED 1
+
+typedef enum { T_COMMAND } plugin_type_t;
+
+/* Bash plugin config. */
+typedef struct bash_plugin_conf
+{
+ const char *path; /* path to binary */
+ char *name; /* Used to distinguish plugins */
+} bash_plugin_conf_t;
+
+/* plugin on_shell_execve function handle type */
+typedef int on_shell_execve_t (char *user, int shell_level, char *cmd, char **argv);
+
+/* plugin plugin_init function handle type */
+typedef int plugin_init_t ();
+
+/* plugin plugin_uninit function handle type */
+typedef int plugin_uninit_t ();
+
+/* Plugin list node. */
+typedef struct plugin_node {
+
+ /* Next plugin pointer. */
+ struct plugin_node *next;
+
+ /* Plugin library handle. */
+ void *plugin_handle;
+
+ /* Plugin on_shell_execve function handle. */
+ on_shell_execve_t *on_shell_execve;
+
+ /* Plugin plugin_init function handle. */
+ plugin_init_t *plugin_init;
+
+ /* Plugin plugin_uninit function handle. */
+ plugin_uninit_t *plugin_uninit;
+} PLUGIN_NODE;
+
+/* Load all plugins */
+extern void load_plugins __P((void));
+
+/* Free all plugins */
+extern void free_plugins __P((void));
+
+/* Invoke plugins before shell execve */
+extern int invoke_plugin_on_shell_execve __P((char *, char *, char **));
+
+#endif /* _PLUGIN_H_ */
diff --git a/bash-5.1/shell.c b/bash-5.1/shell.c
index ce8087f..6928208 100644
--- a/bash-5.1/shell.c
+++ b/bash-5.1/shell.c
@@ -46,6 +46,10 @@
# include <unistd.h>
#endif
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+#include "plugin.h"
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
+
#include "bashintl.h"
#define NEED_SH_SETLINEBUF_DECL /* used in externs.h */
@@ -567,6 +571,10 @@ main (argc, argv, env)
if (shopt_alist)
run_shopt_alist ();
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+ load_plugins ();
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
+
/* From here on in, the shell must be a normal functioning shell.
Variables from the environment are expected to be set, etc. */
shell_initialize ();
@@ -810,6 +818,10 @@ main (argc, argv, env)
/* Read commands until exit condition. */
reader_loop ();
exit_shell (last_command_exit_value);
+
+#if defined (BASH_SHELL_EXECVE_PLUGIN)
+ free_plugins ();
+#endif /* BASH_SHELL_EXECVE_PLUGIN */
}
static int
--
2.17.1.windows.2

1
src/bash/patches/series Normal file
View File

@ -0,0 +1 @@
0001-Add-plugin-support-to-bash.patch