Discussion:
[Libstoragemgmt-devel] [PATCH] [Demo] Allowing plugin run as root.
Gris Ge
2015-01-14 16:51:02 UTC
Permalink
0. This is purely a demo. Please don't commit.

1. The lsmenv has been updated to guide user. Try these command:
sudo <path_to_lsmenv> lsmd
sudo <path_to_lsmenv> sim lsmcli ls
<path_to_lsmenv> sim lsmcli ls

2. Taking 'sim://' plugin as example to let it run as root.
It will print on current user in lsmd console tab.

3. Use libconfig for config file parsing.
It exists in RHEL 6, and RHEL 7 all releases[1]

4. Config layout:
* config/lsmd.conf -- controlling global settings.
allow-plugin-root-privilege = true|false
* config/pluginconf.d/<plug_name>.conf -- plugin level setting.
require-root-privilege = true|false

TODO:
1. Split the warn() fix in lsm_daemon.c to separate patch.
2. Update autotools files about 'config' folder.
3. Update RPM spec file about 'config' folder.
4. Submit the local storage management plugin which require root
privilege with initial methods.
5. Run valgrind to detect memory leak.

Any feedback will be well appreciated.

Thank you in advance.
Best regards.

[1] Checked on:
* RHEL 6 Server
* RHEL 7 Server, s390x, workstation, HPC(aka Node), Client.

Signed-off-by: Gris Ge <***@redhat.com>
---
config/lsmd.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 7 ++
daemon/Makefile.am | 2 +-
daemon/lsm_daemon.c | 198 ++++++++++++++++++++++++++------
packaging/daemon/libstoragemgmt.service | 2 +-
plugin/sim/simarray.py | 1 +
tools/lsmenv | 42 ++++++-
8 files changed, 216 insertions(+), 38 deletions(-)
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/sim.conf

diff --git a/config/lsmd.conf b/config/lsmd.conf
new file mode 100644
index 0000000..b1ceb5f
--- /dev/null
+++ b/config/lsmd.conf
@@ -0,0 +1 @@
+allow-plugin-root-privilege = true
diff --git a/config/pluginconf.d/sim.conf b/config/pluginconf.d/sim.conf
new file mode 100644
index 0000000..7e25b7c
--- /dev/null
+++ b/config/pluginconf.d/sim.conf
@@ -0,0 +1 @@
+require-root-privilege = true
diff --git a/configure.ac b/configure.ac
index 0bd3070..cc40a4e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,6 +181,13 @@ if test "x$with_rest_api" = "xyes"; then
fi
AM_CONDITIONAL([WITH_REST_API], [test "x$with_rest_api" = "xyes"])

+dnl ==========================================================================
+dnl Check for libconfig as it is needed for lsmd daemon
+dnl ==========================================================================
+PKG_CHECK_MODULES(
+ [LIBCONFIG], [libconfig >= 1.3],,
+ AC_MSG_ERROR([libconfig 1.3 or newer not found.]))
+
#Setup the unit directory for systemd stuff
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 521055f..9711d22 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -2,7 +2,7 @@ bin_PROGRAMS = lsmd

EXTRA_DIST=lsm_rest.c lsm_rest.h

-lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie
+lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie -lconfig
lsmd_CFLAGS=-fPIE -DPIE

lsmd_SOURCES = lsm_daemon.c
diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index acc8ef5..cdc27ef 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -17,10 +17,10 @@
* Author: tasleson
*/

+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
-#define _GNU_SOURCE
#include <string.h>
#include <ctype.h>
#include <signal.h>
@@ -43,11 +43,17 @@
#include <assert.h>
#include <grp.h>
#include <limits.h>
+#include <libconfig.h>

#define BASE_DIR "/var/run/lsm"
#define SOCKET_DIR BASE_DIR"/ipc"
#define PLUGIN_DIR "/usr/bin"
#define LSM_USER "libstoragemgmt"
+#define LSM_CONF_DIR "/etc/"
+#define LSM_PLUGIN_CONF_DIR_NAME "pluginconf.d"
+#define LSMD_CONF_FILE "lsmd.conf"
+#define LSM_CONF_ALLOW_ROOT_OPT_NAME "allow-plugin-root-privilege"
+#define LSM_CONF_REQUIRE_ROOT_OPT_NAME "require-root-privilege"

#define min(a,b) \
({ __typeof__ (a) _a = (a); \
@@ -64,19 +70,25 @@ int systemd = 0;

char *socket_dir = SOCKET_DIR;
char *plugin_dir = PLUGIN_DIR;
+char *conf_dir = LSM_CONF_DIR;

char plugin_extension[] = "_lsmplugin";

+char plugin_conf_extension[] = ".conf";
+
typedef enum { RUNNING, RESTART, EXIT } serve_type;
serve_type serve_state = RUNNING;

int plugin_mem_debug = 0;

+int allow_root_plugin = 0;
+
/**
* Each item in plugin list contains this information
*/
struct plugin {
char *file_path;
+ int require_root;
int fd;
LIST_ENTRY(plugin) pointers;
};
@@ -96,7 +108,7 @@ void logger(int severity, const char *fmt, ...)
{
char buf[2048];

- if( (LOG_INFO == severity && verbose_flag) || LOG_ERR == LOG_WARNING
+ if( (LOG_INFO == severity && verbose_flag) || LOG_WARNING == severity
|| LOG_ERR == severity) {
va_list arg;
va_start(arg, fmt);
@@ -150,32 +162,30 @@ void drop_privileges(void)
int err = 0;
struct passwd *pw = NULL;

- if( !systemd ) {
- pw = getpwnam(LSM_USER);
- if( pw ) {
- if ( !geteuid() ) {
+ pw = getpwnam(LSM_USER);
+ if( pw ) {
+ if ( !geteuid() ) {

- if( -1 == setgid(pw->pw_gid) ) {
- err = errno;
- loud("Unexpected error on setgid(errno %d)\n", err);
- }
+ if( -1 == setgid(pw->pw_gid) ) {
+ err = errno;
+ loud("Unexpected error on setgid(errno %d)\n", err);
+ }

- if( -1 == setgroups(1, &pw->pw_gid) ) {
- err = errno;
- loud("Unexpected error on setgroups(errno %d)\n", err);
- }
+ if( -1 == setgroups(1, &pw->pw_gid) ) {
+ err = errno;
+ loud("Unexpected error on setgroups(errno %d)\n", err);
+ }

- if( -1 == setuid(pw->pw_uid) ) {
- err = errno;
- loud("Unexpected error on setuid(errno %d)\n", err);
- }
- } else if ( pw->pw_uid != getuid() ) {
- warn("Daemon not running as correct user\n");
+ if( -1 == setuid(pw->pw_uid) ) {
+ err = errno;
+ loud("Unexpected error on setuid(errno %d)\n", err);
}
- } else {
- info("Warn: Missing %s user, running as existing user!\n",
- LSM_USER);
+ } else if ( pw->pw_uid != getuid() ) {
+ warn("Daemon not running as correct user\n");
}
+ } else {
+ info("Warn: Missing %s user, running as existing user!\n",
+ LSM_USER);
}
}

@@ -208,6 +218,8 @@ void usage(void)
printf(" --plugindir = The directory where the plugins are located\n");
printf(" --socketdir = The directory where the Unix domain sockets will "
"be created\n");
+ printf(" --confdir = The directory where the config files are "
+ "located\n");
printf(" -v = Verbose logging\n");
printf(" -d = new style daemon (systemd)\n");
}
@@ -403,6 +415,40 @@ void empty_plugin_list(struct plugin_list *list)
}

/**
+ * Parse config and seeking provided key name bool
+ * 1. Keep value untouched if file not exist
+ * 2. If file is not readable, abort via loud()
+ * 3. Keep value untouched if provided key not found
+ * 4. Abort via loud() if no enough memory.
+ * @param conf_path config file path
+ * @param key_name string, searching key
+ * @param value int, output, value of this config key
+ */
+
+void parse_conf_bool(char * conf_path, char *key_name, int *value)
+{
+ if( access(conf_path, F_OK) == -1 ) {
+ /* file not exist. */
+ return;
+ }
+ config_t *cfg = (config_t *)malloc(sizeof(config_t));
+ if (cfg){
+ config_init(cfg);
+ if (CONFIG_TRUE == config_read_file(cfg, conf_path)){
+ config_lookup_bool(cfg, key_name, value);
+ }else{
+ loud(
+ "configure %s parsing failed: %s at line %d\n",
+ conf_path, config_error_text(cfg), config_error_line(cfg));
+ }
+ } else{
+ loud("malloc failure while trying to allocate memory for config_t\n");
+ }
+
+ config_destroy(cfg);
+}
+
+/**
* Call back for plug-in processing.
* @param p Private data
* @param full_name Full path and file name
@@ -420,7 +466,40 @@ int process_plugin(void *p, char *full_name)
if( item ) {
item->file_path = strdup(full_name);
item->fd = setup_socket(full_name);
-
+ /* Load plugin configure for root privilege setting */
+ char *base_name = basename(full_name);
+ ssize_t plugin_name_len =
+ strlen(base_name) - strlen(plugin_extension);
+ if ( plugin_name_len <= 0 ){
+ loud("Got invalid plugin full path %s\n", full_name);
+ }
+ ssize_t conf_file_name_len = plugin_name_len +
+ strlen(plugin_conf_extension) + 1;
+ char *plugin_conf_filename =
+ (char *)malloc(conf_file_name_len);
+ if (plugin_conf_filename){
+ strncpy(
+ plugin_conf_filename, base_name, plugin_name_len);
+ strcpy(
+ plugin_conf_filename+plugin_name_len,
+ plugin_conf_extension);
+ plugin_conf_filename[conf_file_name_len] = '\0';
+ }else{
+ loud("malloc failure while trying to allocate %d "
+ "bytes\n", conf_file_name_len);
+ }
+ char *plugin_conf_dir_path =
+ path_form(conf_dir, LSM_PLUGIN_CONF_DIR_NAME);
+
+ char *plugin_conf_path = path_form(
+ plugin_conf_dir_path, plugin_conf_filename);
+ parse_conf_bool(
+ plugin_conf_path, LSM_CONF_REQUIRE_ROOT_OPT_NAME,
+ &item->require_root);
+
+ free(plugin_conf_dir_path);
+ free(plugin_conf_filename);
+ free(plugin_conf_path);
if( item->file_path && item->fd >= 0 ) {
LIST_INSERT_HEAD((struct plugin_list*)p, item, pointers);
info("Plugin %s added\n", full_name);
@@ -456,7 +535,9 @@ void child_cleanup(void)

if( -1 == rc ) {
err = errno;
- warn("Error: waitid %d - %s\n", err, strerror(err));
+ if (err != ECHILD){
+ warn("Error: waitid %d - %s\n", err, strerror(err));
+ }
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
@@ -495,14 +576,14 @@ int process_plugins(void)
/**
* Given a socket descriptor looks it up and returns the plug-in
* @param fd Socket descriptor to lookup
- * @return Character string
+ * @return struct plugin
*/
-char *plugin_lookup(int fd)
+struct plugin *plugin_lookup(int fd)
{
struct plugin *plug = NULL;
LIST_FOREACH(plug, &head, pointers) {
if( plug->fd == fd ) {
- return plug->file_path;
+ return plug;
}
}
return NULL;
@@ -512,8 +593,10 @@ char *plugin_lookup(int fd)
* Does the actual fork and exec of the plug-in
* @param plugin Full filename and path of plug-in to exec.
* @param client_fd Client connected file descriptor
+ * @param require_root int, indicate whether this plugin require root
+ * privilege or not
*/
-void exec_plugin( char *plugin, int client_fd )
+void exec_plugin( char *plugin, int client_fd, int require_root)
{
int err = 0;

@@ -535,6 +618,45 @@ void exec_plugin( char *plugin, int client_fd )
char fd_str[12];
char *plugin_argv[7];
extern char **environ;
+ struct ucred cli_user_cred;
+ socklen_t cli_user_cred_len = sizeof(cli_user_cred);
+
+ /*
+ * The plugin will still run no matter with root privilege or not.
+ * so that client could get detailed error message.
+ */
+ if ( require_root == 0 ){
+ drop_privileges();
+ }else{
+ if (getuid()){
+ warn("Plugin %s require root privilege, but lsmd daemon "
+ "is not run as root user\n", plugin);
+ }else if(allow_root_plugin == 0){
+ warn("Plugin %s require root privilege, but %s disabled "
+ "it globally\n", LSMD_CONF_FILE);
+ drop_privileges();
+ }else{
+ /* Check socket client uid */
+ int rc_get_cli_uid = getsockopt(
+ client_fd, SOL_SOCKET, SO_PEERCRED,
+ &cli_user_cred, &cli_user_cred_len);
+ if( 0 == rc_get_cli_uid){
+ if (cli_user_cred.uid != 0){
+ warn("Plugin %s require root privilege, but "
+ "client is not run as root user\n", plugin);
+ drop_privileges();
+ }else{
+ info("Plugin %s is running as root privilege\n",
+ plugin);
+ }
+ }else{
+ warn("Failed to get client socket uid, getsockopt() "
+ "error: %d\n", errno);
+ drop_privileges();
+ }
+ }
+
+ }

/* Make copy of plug-in string as once we call empty_plugin_list it
* will be deleted :-) */
@@ -617,8 +739,8 @@ void _serving(void)
if( FD_ISSET(fd, &readfds) ) {
int cfd = accept(fd, NULL, NULL);
if( -1 != cfd ) {
- char *p = plugin_lookup(fd);
- exec_plugin(p, cfd);
+ struct plugin *p = plugin_lookup(fd);
+ exec_plugin(p->file_path, cfd, p->require_root);
} else {
err = errno;
info("Error on accepting request: %s", strerror(err));
@@ -659,6 +781,7 @@ int main(int argc, char *argv[])
{"help", no_argument, 0, 'h'}, //Index 0
{"plugindir", required_argument, 0, 0}, //Index 1
{"socketdir", required_argument, 0, 0}, //Index 2
+ {"confdir", required_argument, 0, 0}, //Index 3
{0, 0, 0, 0}
};

@@ -678,6 +801,9 @@ int main(int argc, char *argv[])
case 2:
socket_dir = optarg;
break;
+ case 3:
+ conf_dir = optarg;
+ break;
}
break;

@@ -716,13 +842,21 @@ int main(int argc, char *argv[])
openlog("lsmd", LOG_ODELAY, LOG_USER);
}

+ /* Check lsmd.conf */
+ char *lsmd_conf_path = path_form(conf_dir, LSMD_CONF_FILE);
+ parse_conf_bool(
+ lsmd_conf_path, LSM_CONF_ALLOW_ROOT_OPT_NAME, &allow_root_plugin);
+ free(lsmd_conf_path);
+
/* Check to see if we want to check plugin for memory errors */
if( getenv("LSM_VALGRIND") ) {
plugin_mem_debug = 1;
}

install_sh();
- drop_privileges();
+ if (allow_root_plugin == 0){
+ drop_privileges();
+ }
flight_check();

/* Become a daemon if told we are not using systemd */
diff --git a/packaging/daemon/libstoragemgmt.service b/packaging/daemon/libstoragemgmt.service
index e0dcf98..81a69ae 100644
--- a/packaging/daemon/libstoragemgmt.service
+++ b/packaging/daemon/libstoragemgmt.service
@@ -6,7 +6,7 @@ After=syslog.target
ExecStart=/usr/bin/lsmd -d
ExecReload=/bin/kill -HUP $MAINPID
StandardError=syslog
-User=libstoragemgmt
+User=root

[Install]
WantedBy=multi-user.target
diff --git a/plugin/sim/simarray.py b/plugin/sim/simarray.py
index d733e50..930fc79 100644
--- a/plugin/sim/simarray.py
+++ b/plugin/sim/simarray.py
@@ -910,6 +910,7 @@ class SimData(object):
return self.tmo

def systems(self):
+ print "sim plugin: current plugin uid is: %d" % os.getuid()
return self.syss

def pools(self):
diff --git a/tools/lsmenv b/tools/lsmenv
index da14eed..9667c59 100755
--- a/tools/lsmenv
+++ b/tools/lsmenv
@@ -23,10 +23,12 @@ use Cwd 'abs_path';

$|++;

-my $LSM_UDS_PATH = '/tmp/lsm/ipc/';
-my $BASE_DIR; # Used to store the path of libstoragemgmt-code path.
+my $LSM_UDS_PATH = '/var/run/lsm/ipc/';
+my $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $LSM_URI_FILE = "$ENV{HOME}/.lsm_uri";
my $URI_CFG = Config::IniFiles->new( -file => $LSM_URI_FILE );
+my $LSM_USER = 'libstoragemgmt';
+my $LSM_GROUP = 'libstoragemgmt';

my $REF_PRE_BUILD_URI = {
"sim" => "sim://",
@@ -77,8 +79,33 @@ sub is_in_array($$) {
return undef;
}

+sub chk_lsm_user(){
+ my $code_uid = (stat $BASE_DIR)[4];
+ my $lsm_uid = getpwnam($LSM_USER);
+ if ($lsm_uid and $lsm_uid != $code_uid){
+ print "FAIL: Existing libstoragemgmt account has no access " .
+ "to the code tree.\n";
+ print " Please delete that user and rerun lsmenv as root user\n";
+ exit 1;
+ }
+ unless($lsm_uid){
+ # LSM_USER not exist.
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root user to create a proper " .
+ "$LSM_USER account\n";
+ exit 1;
+ }
+ print "WARN: Creating libstoragemgmt account as shadow account " .
+ "of code tree owner, no login of cource\n";
+ system("groupadd -r libstoragemgmt");
+ system("useradd -r -g libstoragemgmt -d $LSM_UDS_PATH " .
+ "-s /sbin/nologin -u $code_uid --non-unique " .
+ "-c 'daemon account for libstoragemgmt' libstoragemgmt");
+ }
+ return 1;
+}
+
sub lsm_env_setup() {
- $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $py_binding_dir = "$BASE_DIR/python_binding";
my $c_binding_dir = "$BASE_DIR/c_binding";
unless ( ( -e $py_binding_dir ) && ( -e $c_binding_dir ) ) {
@@ -98,6 +125,7 @@ sub lsm_env_setup() {
unless ( -d "$LSM_UDS_PATH" ) {
print "INFO: Creating socket folder $LSM_UDS_PATH\n";
system("mkdir -pv $LSM_UDS_PATH");
+ system("chown libstoragemgmt:libstoragemgmt $LSM_UDS_PATH");
}

$ENV{LSM_UDS_PATH} = $LSM_UDS_PATH;
@@ -171,6 +199,10 @@ sub call_out($) {
}

sub start_lsmd() {
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root to start lsmd\n";
+ exit 1;
+ }
my $lsmd_exec_file = "$BASE_DIR/daemon/lsmd";
unless ( -f $lsmd_exec_file ) {
print "FAIL: $lsmd_exec_file not exist, "
@@ -183,8 +215,9 @@ sub start_lsmd() {
exit 1;
}
my $plugin_dir = "$BASE_DIR/plugin";
+ my $conf_dir = "$BASE_DIR/config";
system( "$lsmd_exec_file --plugindir $plugin_dir "
- . "--socketdir $LSM_UDS_PATH -v -d" );
+ . "--socketdir $LSM_UDS_PATH -v -d --confdir $conf_dir" );
}

sub main() {
@@ -192,6 +225,7 @@ sub main() {
help();
}

+ chk_lsm_user();
lsm_env_setup();
if ( $ARGV[0] eq 'lsmd' ) {
start_lsmd();
--
1.8.3.1
Tony Asleson
2015-01-14 23:59:46 UTC
Permalink
Hi Gris,

Overall, looks pretty good!

Some comments/suggestions:

* sim.conf, require-root-privilege = true, is this just for testing?
The simulator does not need root to run correct?
* I had posted a patch with some fixups to the logging to lsmd, might be
good to pick those up, which I think also fixes the warn fix you mention
- Take the added logic in process_plugin to check for the configuration
option for the plugin and move into separate function
- If we don't have a configuration file for the daemon, perhaps we
should skip checking for config files for each of the plug-ins too?

Thanks,
Tony
Post by Gris Ge
0. This is purely a demo. Please don't commit.
sudo <path_to_lsmenv> lsmd
sudo <path_to_lsmenv> sim lsmcli ls
<path_to_lsmenv> sim lsmcli ls
2. Taking 'sim://' plugin as example to let it run as root.
It will print on current user in lsmd console tab.
3. Use libconfig for config file parsing.
It exists in RHEL 6, and RHEL 7 all releases[1]
* config/lsmd.conf -- controlling global settings.
allow-plugin-root-privilege = true|false
* config/pluginconf.d/<plug_name>.conf -- plugin level setting.
require-root-privilege = true|false
1. Split the warn() fix in lsm_daemon.c to separate patch.
2. Update autotools files about 'config' folder.
3. Update RPM spec file about 'config' folder.
4. Submit the local storage management plugin which require root
privilege with initial methods.
5. Run valgrind to detect memory leak.
Any feedback will be well appreciated.
Thank you in advance.
Best regards.
* RHEL 6 Server
* RHEL 7 Server, s390x, workstation, HPC(aka Node), Client.
---
config/lsmd.conf | 1 +
config/pluginconf.d/sim.conf | 1 +
configure.ac | 7 ++
daemon/Makefile.am | 2 +-
daemon/lsm_daemon.c | 198 ++++++++++++++++++++++++++------
packaging/daemon/libstoragemgmt.service | 2 +-
plugin/sim/simarray.py | 1 +
tools/lsmenv | 42 ++++++-
8 files changed, 216 insertions(+), 38 deletions(-)
create mode 100644 config/lsmd.conf
create mode 100644 config/pluginconf.d/sim.conf
diff --git a/config/lsmd.conf b/config/lsmd.conf
new file mode 100644
index 0000000..b1ceb5f
--- /dev/null
+++ b/config/lsmd.conf
@@ -0,0 +1 @@
+allow-plugin-root-privilege = true
diff --git a/config/pluginconf.d/sim.conf b/config/pluginconf.d/sim.conf
new file mode 100644
index 0000000..7e25b7c
--- /dev/null
+++ b/config/pluginconf.d/sim.conf
@@ -0,0 +1 @@
+require-root-privilege = true
diff --git a/configure.ac b/configure.ac
index 0bd3070..cc40a4e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -181,6 +181,13 @@ if test "x$with_rest_api" = "xyes"; then
fi
AM_CONDITIONAL([WITH_REST_API], [test "x$with_rest_api" = "xyes"])
+dnl ==========================================================================
+dnl Check for libconfig as it is needed for lsmd daemon
+dnl ==========================================================================
+PKG_CHECK_MODULES(
+ [LIBCONFIG], [libconfig >= 1.3],,
+ AC_MSG_ERROR([libconfig 1.3 or newer not found.]))
+
#Setup the unit directory for systemd stuff
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 521055f..9711d22 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -2,7 +2,7 @@ bin_PROGRAMS = lsmd
EXTRA_DIST=lsm_rest.c lsm_rest.h
-lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie
+lsmd_LDFLAGS=-Wl,-z,relro,-z,now -pie -lconfig
lsmd_CFLAGS=-fPIE -DPIE
lsmd_SOURCES = lsm_daemon.c
diff --git a/daemon/lsm_daemon.c b/daemon/lsm_daemon.c
index acc8ef5..cdc27ef 100644
--- a/daemon/lsm_daemon.c
+++ b/daemon/lsm_daemon.c
@@ -17,10 +17,10 @@
* Author: tasleson
*/
+#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
-#define _GNU_SOURCE
#include <string.h>
#include <ctype.h>
#include <signal.h>
@@ -43,11 +43,17 @@
#include <assert.h>
#include <grp.h>
#include <limits.h>
+#include <libconfig.h>
#define BASE_DIR "/var/run/lsm"
#define SOCKET_DIR BASE_DIR"/ipc"
#define PLUGIN_DIR "/usr/bin"
#define LSM_USER "libstoragemgmt"
+#define LSM_CONF_DIR "/etc/"
+#define LSM_PLUGIN_CONF_DIR_NAME "pluginconf.d"
+#define LSMD_CONF_FILE "lsmd.conf"
+#define LSM_CONF_ALLOW_ROOT_OPT_NAME "allow-plugin-root-privilege"
+#define LSM_CONF_REQUIRE_ROOT_OPT_NAME "require-root-privilege"
#define min(a,b) \
({ __typeof__ (a) _a = (a); \
@@ -64,19 +70,25 @@ int systemd = 0;
char *socket_dir = SOCKET_DIR;
char *plugin_dir = PLUGIN_DIR;
+char *conf_dir = LSM_CONF_DIR;
char plugin_extension[] = "_lsmplugin";
+char plugin_conf_extension[] = ".conf";
+
typedef enum { RUNNING, RESTART, EXIT } serve_type;
serve_type serve_state = RUNNING;
int plugin_mem_debug = 0;
+int allow_root_plugin = 0;
+
/**
* Each item in plugin list contains this information
*/
struct plugin {
char *file_path;
+ int require_root;
int fd;
LIST_ENTRY(plugin) pointers;
};
@@ -96,7 +108,7 @@ void logger(int severity, const char *fmt, ...)
{
char buf[2048];
- if( (LOG_INFO == severity && verbose_flag) || LOG_ERR == LOG_WARNING
+ if( (LOG_INFO == severity && verbose_flag) || LOG_WARNING == severity
|| LOG_ERR == severity) {
va_list arg;
va_start(arg, fmt);
@@ -150,32 +162,30 @@ void drop_privileges(void)
int err = 0;
struct passwd *pw = NULL;
- if( !systemd ) {
- pw = getpwnam(LSM_USER);
- if( pw ) {
- if ( !geteuid() ) {
+ pw = getpwnam(LSM_USER);
+ if( pw ) {
+ if ( !geteuid() ) {
- if( -1 == setgid(pw->pw_gid) ) {
- err = errno;
- loud("Unexpected error on setgid(errno %d)\n", err);
- }
+ if( -1 == setgid(pw->pw_gid) ) {
+ err = errno;
+ loud("Unexpected error on setgid(errno %d)\n", err);
+ }
- if( -1 == setgroups(1, &pw->pw_gid) ) {
- err = errno;
- loud("Unexpected error on setgroups(errno %d)\n", err);
- }
+ if( -1 == setgroups(1, &pw->pw_gid) ) {
+ err = errno;
+ loud("Unexpected error on setgroups(errno %d)\n", err);
+ }
- if( -1 == setuid(pw->pw_uid) ) {
- err = errno;
- loud("Unexpected error on setuid(errno %d)\n", err);
- }
- } else if ( pw->pw_uid != getuid() ) {
- warn("Daemon not running as correct user\n");
+ if( -1 == setuid(pw->pw_uid) ) {
+ err = errno;
+ loud("Unexpected error on setuid(errno %d)\n", err);
}
- } else {
- info("Warn: Missing %s user, running as existing user!\n",
- LSM_USER);
+ } else if ( pw->pw_uid != getuid() ) {
+ warn("Daemon not running as correct user\n");
}
+ } else {
+ info("Warn: Missing %s user, running as existing user!\n",
+ LSM_USER);
}
}
@@ -208,6 +218,8 @@ void usage(void)
printf(" --plugindir = The directory where the plugins are located\n");
printf(" --socketdir = The directory where the Unix domain sockets will "
"be created\n");
+ printf(" --confdir = The directory where the config files are "
+ "located\n");
printf(" -v = Verbose logging\n");
printf(" -d = new style daemon (systemd)\n");
}
@@ -403,6 +415,40 @@ void empty_plugin_list(struct plugin_list *list)
}
/**
+ * Parse config and seeking provided key name bool
+ * 1. Keep value untouched if file not exist
+ * 2. If file is not readable, abort via loud()
+ * 3. Keep value untouched if provided key not found
+ * 4. Abort via loud() if no enough memory.
+ */
+
+void parse_conf_bool(char * conf_path, char *key_name, int *value)
+{
+ if( access(conf_path, F_OK) == -1 ) {
+ /* file not exist. */
+ return;
+ }
+ config_t *cfg = (config_t *)malloc(sizeof(config_t));
+ if (cfg){
+ config_init(cfg);
+ if (CONFIG_TRUE == config_read_file(cfg, conf_path)){
+ config_lookup_bool(cfg, key_name, value);
+ }else{
+ loud(
+ "configure %s parsing failed: %s at line %d\n",
+ conf_path, config_error_text(cfg), config_error_line(cfg));
+ }
+ } else{
+ loud("malloc failure while trying to allocate memory for config_t\n");
+ }
+
+ config_destroy(cfg);
+}
+
+/**
* Call back for plug-in processing.
@@ -420,7 +466,40 @@ int process_plugin(void *p, char *full_name)
if( item ) {
item->file_path = strdup(full_name);
item->fd = setup_socket(full_name);
-
+ /* Load plugin configure for root privilege setting */
+ char *base_name = basename(full_name);
+ ssize_t plugin_name_len =
+ strlen(base_name) - strlen(plugin_extension);
+ if ( plugin_name_len <= 0 ){
+ loud("Got invalid plugin full path %s\n", full_name);
+ }
+ ssize_t conf_file_name_len = plugin_name_len +
+ strlen(plugin_conf_extension) + 1;
+ char *plugin_conf_filename =
+ (char *)malloc(conf_file_name_len);
+ if (plugin_conf_filename){
+ strncpy(
+ plugin_conf_filename, base_name, plugin_name_len);
+ strcpy(
+ plugin_conf_filename+plugin_name_len,
+ plugin_conf_extension);
+ plugin_conf_filename[conf_file_name_len] = '\0';
+ }else{
+ loud("malloc failure while trying to allocate %d "
+ "bytes\n", conf_file_name_len);
+ }
+ char *plugin_conf_dir_path =
+ path_form(conf_dir, LSM_PLUGIN_CONF_DIR_NAME);
+
+ char *plugin_conf_path = path_form(
+ plugin_conf_dir_path, plugin_conf_filename);
+ parse_conf_bool(
+ plugin_conf_path, LSM_CONF_REQUIRE_ROOT_OPT_NAME,
+ &item->require_root);
+
+ free(plugin_conf_dir_path);
+ free(plugin_conf_filename);
+ free(plugin_conf_path);
if( item->file_path && item->fd >= 0 ) {
LIST_INSERT_HEAD((struct plugin_list*)p, item, pointers);
info("Plugin %s added\n", full_name);
@@ -456,7 +535,9 @@ void child_cleanup(void)
if( -1 == rc ) {
err = errno;
- warn("Error: waitid %d - %s\n", err, strerror(err));
+ if (err != ECHILD){
+ warn("Error: waitid %d - %s\n", err, strerror(err));
+ }
break;
} else {
if( 0 == rc && si.si_pid == 0 ) {
@@ -495,14 +576,14 @@ int process_plugins(void)
/**
* Given a socket descriptor looks it up and returns the plug-in
*/
-char *plugin_lookup(int fd)
+struct plugin *plugin_lookup(int fd)
{
struct plugin *plug = NULL;
LIST_FOREACH(plug, &head, pointers) {
if( plug->fd == fd ) {
- return plug->file_path;
+ return plug;
}
}
return NULL;
@@ -512,8 +593,10 @@ char *plugin_lookup(int fd)
* Does the actual fork and exec of the plug-in
+ * privilege or not
*/
-void exec_plugin( char *plugin, int client_fd )
+void exec_plugin( char *plugin, int client_fd, int require_root)
{
int err = 0;
@@ -535,6 +618,45 @@ void exec_plugin( char *plugin, int client_fd )
char fd_str[12];
char *plugin_argv[7];
extern char **environ;
+ struct ucred cli_user_cred;
+ socklen_t cli_user_cred_len = sizeof(cli_user_cred);
+
+ /*
+ * The plugin will still run no matter with root privilege or not.
+ * so that client could get detailed error message.
+ */
+ if ( require_root == 0 ){
+ drop_privileges();
+ }else{
+ if (getuid()){
+ warn("Plugin %s require root privilege, but lsmd daemon "
+ "is not run as root user\n", plugin);
+ }else if(allow_root_plugin == 0){
+ warn("Plugin %s require root privilege, but %s disabled "
+ "it globally\n", LSMD_CONF_FILE);
+ drop_privileges();
+ }else{
+ /* Check socket client uid */
+ int rc_get_cli_uid = getsockopt(
+ client_fd, SOL_SOCKET, SO_PEERCRED,
+ &cli_user_cred, &cli_user_cred_len);
+ if( 0 == rc_get_cli_uid){
+ if (cli_user_cred.uid != 0){
+ warn("Plugin %s require root privilege, but "
+ "client is not run as root user\n", plugin);
+ drop_privileges();
+ }else{
+ info("Plugin %s is running as root privilege\n",
+ plugin);
+ }
+ }else{
+ warn("Failed to get client socket uid, getsockopt() "
+ "error: %d\n", errno);
+ drop_privileges();
+ }
+ }
+
+ }
/* Make copy of plug-in string as once we call empty_plugin_list it
* will be deleted :-) */
@@ -617,8 +739,8 @@ void _serving(void)
if( FD_ISSET(fd, &readfds) ) {
int cfd = accept(fd, NULL, NULL);
if( -1 != cfd ) {
- char *p = plugin_lookup(fd);
- exec_plugin(p, cfd);
+ struct plugin *p = plugin_lookup(fd);
+ exec_plugin(p->file_path, cfd, p->require_root);
} else {
err = errno;
info("Error on accepting request: %s", strerror(err));
@@ -659,6 +781,7 @@ int main(int argc, char *argv[])
{"help", no_argument, 0, 'h'}, //Index 0
{"plugindir", required_argument, 0, 0}, //Index 1
{"socketdir", required_argument, 0, 0}, //Index 2
+ {"confdir", required_argument, 0, 0}, //Index 3
{0, 0, 0, 0}
};
@@ -678,6 +801,9 @@ int main(int argc, char *argv[])
socket_dir = optarg;
break;
+ conf_dir = optarg;
+ break;
}
break;
@@ -716,13 +842,21 @@ int main(int argc, char *argv[])
openlog("lsmd", LOG_ODELAY, LOG_USER);
}
+ /* Check lsmd.conf */
+ char *lsmd_conf_path = path_form(conf_dir, LSMD_CONF_FILE);
+ parse_conf_bool(
+ lsmd_conf_path, LSM_CONF_ALLOW_ROOT_OPT_NAME, &allow_root_plugin);
+ free(lsmd_conf_path);
+
/* Check to see if we want to check plugin for memory errors */
if( getenv("LSM_VALGRIND") ) {
plugin_mem_debug = 1;
}
install_sh();
- drop_privileges();
+ if (allow_root_plugin == 0){
+ drop_privileges();
+ }
flight_check();
/* Become a daemon if told we are not using systemd */
diff --git a/packaging/daemon/libstoragemgmt.service b/packaging/daemon/libstoragemgmt.service
index e0dcf98..81a69ae 100644
--- a/packaging/daemon/libstoragemgmt.service
+++ b/packaging/daemon/libstoragemgmt.service
@@ -6,7 +6,7 @@ After=syslog.target
ExecStart=/usr/bin/lsmd -d
ExecReload=/bin/kill -HUP $MAINPID
StandardError=syslog
-User=libstoragemgmt
+User=root
[Install]
WantedBy=multi-user.target
diff --git a/plugin/sim/simarray.py b/plugin/sim/simarray.py
index d733e50..930fc79 100644
--- a/plugin/sim/simarray.py
+++ b/plugin/sim/simarray.py
return self.tmo
+ print "sim plugin: current plugin uid is: %d" % os.getuid()
return self.syss
diff --git a/tools/lsmenv b/tools/lsmenv
index da14eed..9667c59 100755
--- a/tools/lsmenv
+++ b/tools/lsmenv
@@ -23,10 +23,12 @@ use Cwd 'abs_path';
$|++;
-my $LSM_UDS_PATH = '/tmp/lsm/ipc/';
-my $BASE_DIR; # Used to store the path of libstoragemgmt-code path.
+my $LSM_UDS_PATH = '/var/run/lsm/ipc/';
+my $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $LSM_URI_FILE = "$ENV{HOME}/.lsm_uri";
my $URI_CFG = Config::IniFiles->new( -file => $LSM_URI_FILE );
+my $LSM_USER = 'libstoragemgmt';
+my $LSM_GROUP = 'libstoragemgmt';
my $REF_PRE_BUILD_URI = {
"sim" => "sim://",
@@ -77,8 +79,33 @@ sub is_in_array($$) {
return undef;
}
+sub chk_lsm_user(){
+ my $code_uid = (stat $BASE_DIR)[4];
+ my $lsm_uid = getpwnam($LSM_USER);
+ if ($lsm_uid and $lsm_uid != $code_uid){
+ print "FAIL: Existing libstoragemgmt account has no access " .
+ "to the code tree.\n";
+ print " Please delete that user and rerun lsmenv as root user\n";
+ exit 1;
+ }
+ unless($lsm_uid){
+ # LSM_USER not exist.
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root user to create a proper " .
+ "$LSM_USER account\n";
+ exit 1;
+ }
+ print "WARN: Creating libstoragemgmt account as shadow account " .
+ "of code tree owner, no login of cource\n";
+ system("groupadd -r libstoragemgmt");
+ system("useradd -r -g libstoragemgmt -d $LSM_UDS_PATH " .
+ "-s /sbin/nologin -u $code_uid --non-unique " .
+ "-c 'daemon account for libstoragemgmt' libstoragemgmt");
+ }
+ return 1;
+}
+
sub lsm_env_setup() {
- $BASE_DIR = dirname( dirname( abs_path($0) ) );
my $py_binding_dir = "$BASE_DIR/python_binding";
my $c_binding_dir = "$BASE_DIR/c_binding";
unless ( ( -e $py_binding_dir ) && ( -e $c_binding_dir ) ) {
@@ -98,6 +125,7 @@ sub lsm_env_setup() {
unless ( -d "$LSM_UDS_PATH" ) {
print "INFO: Creating socket folder $LSM_UDS_PATH\n";
system("mkdir -pv $LSM_UDS_PATH");
+ system("chown libstoragemgmt:libstoragemgmt $LSM_UDS_PATH");
}
$ENV{LSM_UDS_PATH} = $LSM_UDS_PATH;
@@ -171,6 +199,10 @@ sub call_out($) {
}
sub start_lsmd() {
+ if ($> != 0){
+ print "FAIL: Please run lsmenv as root to start lsmd\n";
+ exit 1;
+ }
my $lsmd_exec_file = "$BASE_DIR/daemon/lsmd";
unless ( -f $lsmd_exec_file ) {
print "FAIL: $lsmd_exec_file not exist, "
@@ -183,8 +215,9 @@ sub start_lsmd() {
exit 1;
}
my $plugin_dir = "$BASE_DIR/plugin";
+ my $conf_dir = "$BASE_DIR/config";
system( "$lsmd_exec_file --plugindir $plugin_dir "
- . "--socketdir $LSM_UDS_PATH -v -d" );
+ . "--socketdir $LSM_UDS_PATH -v -d --confdir $conf_dir" );
}
sub main() {
@@ -192,6 +225,7 @@ sub main() {
help();
}
+ chk_lsm_user();
lsm_env_setup();
if ( $ARGV[0] eq 'lsmd' ) {
start_lsmd();
Gris Ge
2015-01-15 08:31:55 UTC
Permalink
Post by Tony Asleson
Hi Gris,
Hi Tony,
Post by Tony Asleson
Overall, looks pretty good!
* sim.conf, require-root-privilege = true, is this just for testing?
The simulator does not need root to run correct?
Just for test. It will not included in real patch.
Post by Tony Asleson
* I had posted a patch with some fixups to the logging to lsmd, might be
good to pick those up, which I think also fixes the warn fix you mention
Thanks.
Post by Tony Asleson
- Take the added logic in process_plugin to check for the configuration
option for the plugin and move into separate function
Will do.
Post by Tony Asleson
- If we don't have a configuration file for the daemon, perhaps we
should skip checking for config files for each of the plug-ins too?
Will do.
Post by Tony Asleson
Thanks,
Tony
Thank you very much for the suggestions.
Best regards.
--
Gris Ge
Loading...