[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:
parent
32e73b0872
commit
7d40384c58
@ -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/
|
||||
|
@ -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
|
||||
|
||||
|
3
slave.mk
3
slave.mk
@ -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)) \
|
||||
|
@ -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 \
|
||||
|
@ -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 \
|
||||
|
@ -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" -%}
|
||||
|
@ -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
4
src/bash/.gitignore
vendored
@ -1,3 +1,7 @@
|
||||
*
|
||||
!.gitignore
|
||||
!Makefile
|
||||
!Files/
|
||||
!Files/*
|
||||
!patches/
|
||||
!patches/*
|
||||
|
18
src/bash/Files/unittest/Makefile
Normal file
18
src/bash/Files/unittest/Makefile
Normal 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
|
6
src/bash/Files/unittest/bash_plugins.conf
Normal file
6
src/bash/Files/unittest/bash_plugins.conf
Normal 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
|
218
src/bash/Files/unittest/mock_helper.c
Normal file
218
src/bash/Files/unittest/mock_helper.c
Normal 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);
|
||||
}
|
65
src/bash/Files/unittest/mock_helper.h
Normal file
65
src/bash/Files/unittest/mock_helper.h
Normal 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_ */
|
217
src/bash/Files/unittest/plugin_test.c
Normal file
217
src/bash/Files/unittest/plugin_test.c
Normal 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();
|
||||
}
|
@ -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)/
|
||||
|
805
src/bash/patches/0001-Add-plugin-support-to-bash.patch
Normal file
805
src/bash/patches/0001-Add-plugin-support-to-bash.patch
Normal 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
1
src/bash/patches/series
Normal file
@ -0,0 +1 @@
|
||||
0001-Add-plugin-support-to-bash.patch
|
Loading…
Reference in New Issue
Block a user