00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #define _GNU_SOURCE
00020 #include <stdio.h>
00021 #include <stdlib.h>
00022 #include <getopt.h>
00023 #include <string.h>
00024 #include <ctype.h>
00025 #include <signal.h>
00026 #include <sys/types.h>
00027 #include <pwd.h>
00028 #include <errno.h>
00029 #include <unistd.h>
00030 #include <sys/types.h>
00031 #include <syslog.h>
00032 #include <stdarg.h>
00033 #include <dirent.h>
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <sys/socket.h>
00037 #include <sys/un.h>
00038 #include <sys/queue.h>
00039 #include <sys/wait.h>
00040 #include <sys/time.h>
00041 #include <libgen.h>
00042 #include <assert.h>
00043 #include <grp.h>
00044 #include <limits.h>
00045 #include <libconfig.h>
00046
00047 #define BASE_DIR "/var/run/lsm"
00048 #define SOCKET_DIR BASE_DIR"/ipc"
00049 #define PLUGIN_DIR "/usr/bin"
00050 #define LSM_USER "libstoragemgmt"
00051 #define LSM_CONF_DIR "/etc/lsm/"
00052 #define LSM_PLUGIN_CONF_DIR_NAME "pluginconf.d"
00053 #define LSMD_CONF_FILE "lsmd.conf"
00054 #define LSM_CONF_ALLOW_ROOT_OPT_NAME "allow-plugin-root-privilege"
00055 #define LSM_CONF_REQUIRE_ROOT_OPT_NAME "require-root-privilege"
00056
00057 #define min(a,b) \
00058 ({ __typeof__ (a) _a = (a); \
00059 __typeof__ (b) _b = (b); \
00060 _a < _b ? _a : _b; })
00061
00062 #define max(a,b) \
00063 ({ __typeof__ (a) _a = (a); \
00064 __typeof__ (b) _b = (b); \
00065 _a > _b ? _a : _b; })
00066
00067 int verbose_flag = 0;
00068 int systemd = 0;
00069
00070 char *socket_dir = SOCKET_DIR;
00071 char *plugin_dir = PLUGIN_DIR;
00072 char *conf_dir = LSM_CONF_DIR;
00073
00074 char plugin_extension[] = "_lsmplugin";
00075
00076 char plugin_conf_extension[] = ".conf";
00077
00078 typedef enum { RUNNING, RESTART, EXIT } serve_type;
00079 serve_type serve_state = RUNNING;
00080
00081 int plugin_mem_debug = 0;
00082
00083 int allow_root_plugin = 0;
00084 int has_root_plugin = 0;
00085
00089 struct plugin {
00090 char *file_path;
00091 int require_root;
00092 int fd;
00093 LIST_ENTRY(plugin) pointers;
00094 };
00095
00099 LIST_HEAD(plugin_list, plugin) head;
00100
00107 void logger(int severity, const char *fmt, ...)
00108 {
00109 char buf[2048];
00110
00111 if (verbose_flag || LOG_WARNING == severity || LOG_ERR == severity) {
00112 va_list arg;
00113 va_start(arg, fmt);
00114 vsnprintf(buf, sizeof(buf), fmt, arg);
00115 va_end(arg);
00116
00117 if (!systemd) {
00118 if (verbose_flag) {
00119 syslog(LOG_ERR, "%s", buf);
00120 } else {
00121 syslog(severity, "%s", buf);
00122 }
00123 } else {
00124 fprintf(stdout, "%s", buf);
00125 fflush(stdout);
00126 }
00127
00128 if (LOG_ERR == severity) {
00129 exit(1);
00130 }
00131 }
00132 }
00133
00134 #define log_and_exit(fmt, ...) logger(LOG_ERR, fmt, ##__VA_ARGS__)
00135 #define warn(fmt, ...) logger(LOG_WARNING, fmt, ##__VA_ARGS__)
00136 #define info(fmt, ...) logger(LOG_INFO, fmt, ##__VA_ARGS__)
00137
00142 void signal_handler(int s)
00143 {
00144 if (SIGTERM == s) {
00145 serve_state = EXIT;
00146 } else if (SIGHUP == s) {
00147 serve_state = RESTART;
00148 }
00149 }
00150
00154 void install_sh(void)
00155 {
00156 if (signal(SIGTERM, signal_handler) == SIG_ERR) {
00157 log_and_exit("Can't catch signal SIGTERM\n");
00158 }
00159
00160 if (signal(SIGHUP, signal_handler) == SIG_ERR) {
00161 log_and_exit("Can't catch signal SIGHUP\n");
00162 }
00163 }
00164
00169 void drop_privileges(void)
00170 {
00171 int err = 0;
00172 struct passwd *pw = NULL;
00173
00174 pw = getpwnam(LSM_USER);
00175 if (pw) {
00176 if (!geteuid()) {
00177
00178 if (-1 == setgid(pw->pw_gid)) {
00179 err = errno;
00180 log_and_exit("Unexpected error on setgid(errno %d)\n", err);
00181 }
00182
00183 if (-1 == setgroups(1, &pw->pw_gid)) {
00184 err = errno;
00185 log_and_exit("Unexpected error on setgroups(errno %d)\n", err);
00186 }
00187
00188 if (-1 == setuid(pw->pw_uid)) {
00189 err = errno;
00190 log_and_exit("Unexpected error on setuid(errno %d)\n", err);
00191 }
00192 } else if (pw->pw_uid != getuid()) {
00193 warn("Daemon not running as correct user\n");
00194 }
00195 } else {
00196 info("Warn: Missing %s user, running as existing user!\n", LSM_USER);
00197 }
00198 }
00199
00203 void flight_check(void)
00204 {
00205 int err = 0;
00206 if (-1 == access(socket_dir, R_OK | W_OK)) {
00207 err = errno;
00208 log_and_exit("Unable to access socket directory %s, errno= %d\n",
00209 socket_dir, err);
00210 }
00211
00212 if (-1 == access(plugin_dir, R_OK | X_OK)) {
00213 err = errno;
00214 log_and_exit("Unable to access plug-in directory %s, errno= %d\n",
00215 plugin_dir, err);
00216 }
00217 }
00218
00222 void usage(void)
00223 {
00224 printf("libStorageMgmt plug-in daemon.\n");
00225 printf("lsmd [--plugindir <directory>] [--socketdir <dir>] [-v] [-d]\n");
00226 printf(" --plugindir = The directory where the plugins are located\n");
00227 printf(" --socketdir = The directory where the Unix domain sockets will "
00228 "be created\n");
00229 printf(" --confdir = The directory where the config files are "
00230 "located\n");
00231 printf(" -v = Verbose logging\n");
00232 printf(" -d = new style daemon (systemd)\n");
00233 }
00234
00241 char *path_form(const char *path, const char *name)
00242 {
00243 size_t s = strlen(path) + strlen(name) + 2;
00244 char *full = calloc(1, s);
00245 if (full) {
00246 snprintf(full, s, "%s/%s", path, name);
00247 } else {
00248 log_and_exit("malloc failure while trying to allocate %d bytes\n", s);
00249 }
00250 return full;
00251 }
00252
00253
00254 typedef int (*file_op) (void *p, char *full_file_path);
00255
00264 void process_directory(char *dir, void *p, file_op call_back)
00265 {
00266 int err = 0;
00267
00268 if (call_back && dir && strlen(dir)) {
00269 DIR *dp = NULL;
00270 struct dirent *entry = NULL;
00271 char *full_name = NULL;
00272 dp = opendir(dir);
00273
00274 if (dp) {
00275 while ((entry = readdir(dp)) != NULL) {
00276 struct stat entry_st;
00277 free(full_name);
00278 full_name = path_form(dir, entry->d_name);
00279
00280 if (lstat(full_name, &entry_st) != 0) {
00281 continue;
00282 }
00283
00284 if (S_ISDIR(entry_st.st_mode)) {
00285 if (strncmp(entry->d_name, ".", 1) == 0) {
00286 continue;
00287 }
00288 process_directory(full_name, p, call_back);
00289 } else {
00290 if (call_back(p, full_name)) {
00291 break;
00292 }
00293 }
00294 }
00295
00296 free(full_name);
00297
00298 if (closedir(dp)) {
00299 err = errno;
00300 log_and_exit("Error on closing dir %s: %s\n", dir,
00301 strerror(err));
00302 }
00303 } else {
00304 err = errno;
00305 log_and_exit("Error on processing directory %s: %s\n", dir,
00306 strerror(err));
00307 }
00308 }
00309 }
00310
00317 int delete_socket(void *p, char *full_name)
00318 {
00319 struct stat statbuf;
00320 int err;
00321
00322 assert(p == NULL);
00323
00324 if (!lstat(full_name, &statbuf)) {
00325 if (S_ISSOCK(statbuf.st_mode)) {
00326 if (unlink(full_name)) {
00327 err = errno;
00328 log_and_exit("Error on unlinking file %s: %s\n",
00329 full_name, strerror(err));
00330 }
00331 }
00332 }
00333 return 0;
00334 }
00335
00339 void clean_sockets()
00340 {
00341 process_directory(socket_dir, NULL, delete_socket);
00342 }
00343
00344
00350 int setup_socket(char *full_name)
00351 {
00352 int err = 0;
00353 char name[128];
00354
00355
00356
00357 memset(name, 0, sizeof(name));
00358
00359 char *base_nm = basename(full_name);
00360 strncpy(name, base_nm,
00361 min(abs(strlen(base_nm) - strlen(plugin_extension)),
00362 (sizeof(name) - 1)));
00363
00364 char *socket_file = path_form(socket_dir, name);
00365 delete_socket(NULL, socket_file);
00366
00367 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
00368 if (-1 != fd) {
00369 struct sockaddr_un addr;
00370 memset(&addr, 0, sizeof(addr));
00371 addr.sun_family = AF_UNIX;
00372
00373 strncpy(addr.sun_path, socket_file, sizeof(addr.sun_path) - 1);
00374
00375 if (-1 ==
00376 bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un))) {
00377 err = errno;
00378 log_and_exit("Error on binding socket %s: %s\n", socket_file,
00379 strerror(err));
00380 }
00381
00382 if (-1 == chmod(socket_file, S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP
00383 | S_IROTH | S_IWOTH)) {
00384 err = errno;
00385 log_and_exit("Error on chmod socket file %s: %s\n", socket_file,
00386 strerror(err));
00387 }
00388
00389 if (-1 == listen(fd, 5)) {
00390 err = errno;
00391 log_and_exit("Error on listening %s: %s\n", socket_file,
00392 strerror(err));
00393 }
00394
00395 } else {
00396 err = errno;
00397 log_and_exit("Error on socket create %s: %s\n",
00398 socket_file, strerror(err));
00399 }
00400
00401 free(socket_file);
00402 return fd;
00403 }
00404
00409 void empty_plugin_list(struct plugin_list *list)
00410 {
00411 int err;
00412 struct plugin *item = NULL;
00413
00414 while (!LIST_EMPTY(list)) {
00415 item = LIST_FIRST(list);
00416 LIST_REMOVE(item, pointers);
00417
00418 if (-1 == close(item->fd)) {
00419 err = errno;
00420 info("Error on closing fd %d for file %s: %s\n", item->fd,
00421 item->file_path, strerror(err));
00422 }
00423
00424 free(item->file_path);
00425 item->file_path = NULL;
00426 item->fd = INT_MAX;
00427 free(item);
00428 }
00429 }
00430
00442 void parse_conf_bool(char *conf_path, char *key_name, int *value)
00443 {
00444 if (access(conf_path, F_OK) == -1) {
00445
00446 return;
00447 }
00448 config_t *cfg = (config_t *) malloc(sizeof(config_t));
00449 if (cfg) {
00450 config_init(cfg);
00451 if (CONFIG_TRUE == config_read_file(cfg, conf_path)) {
00452 config_lookup_bool(cfg, key_name, value);
00453 } else {
00454 log_and_exit("configure %s parsing failed: %s at line %d\n",
00455 conf_path, config_error_text(cfg),
00456 config_error_line(cfg));
00457 }
00458 } else {
00459 log_and_exit
00460 ("malloc failure while trying to allocate memory for config_t\n");
00461 }
00462
00463 config_destroy(cfg);
00464 free(cfg);
00465 }
00466
00474 int chk_pconf_root_pri(char *plugin_path)
00475 {
00476 int require_root = 0;
00477 char *base_name = basename(plugin_path);
00478 ssize_t plugin_name_len = strlen(base_name) - strlen(plugin_extension);
00479 if (plugin_name_len <= 0) {
00480 log_and_exit("Got invalid plugin full path %s\n", plugin_path);
00481 }
00482 ssize_t conf_file_name_len = plugin_name_len +
00483 strlen(plugin_conf_extension) + 1;
00484 char *plugin_conf_filename = (char *) malloc(conf_file_name_len);
00485 if (plugin_conf_filename) {
00486 strncpy(plugin_conf_filename, base_name, plugin_name_len);
00487 strncpy(plugin_conf_filename + plugin_name_len,
00488 plugin_conf_extension, strlen(plugin_conf_extension));
00489 plugin_conf_filename[conf_file_name_len - 1] = '\0';
00490
00491 char *plugin_conf_dir_path = path_form(conf_dir,
00492 LSM_PLUGIN_CONF_DIR_NAME);
00493
00494 char *plugin_conf_path = path_form(plugin_conf_dir_path,
00495 plugin_conf_filename);
00496 parse_conf_bool(plugin_conf_path, LSM_CONF_REQUIRE_ROOT_OPT_NAME,
00497 &require_root);
00498
00499 if (require_root == 1 && allow_root_plugin == 0) {
00500 warn("Plugin %s require root privilege while %s disable globally\n",
00501 base_name, LSMD_CONF_FILE);
00502 }
00503 free(plugin_conf_dir_path);
00504 free(plugin_conf_filename);
00505 free(plugin_conf_path);
00506 } else {
00507 log_and_exit("malloc failure while trying to allocate %d "
00508 "bytes\n", conf_file_name_len);
00509 }
00510 return require_root;
00511 }
00512
00519 int process_plugin(void *p, char *full_name)
00520 {
00521 if (full_name) {
00522 size_t ext_len = strlen(plugin_extension);
00523 size_t full_len = strlen(full_name);
00524
00525 if (full_len > ext_len) {
00526 if (strncmp
00527 (full_name + full_len - ext_len, plugin_extension,
00528 ext_len) == 0) {
00529 struct plugin *item = calloc(1, sizeof(struct plugin));
00530 if (item) {
00531 item->file_path = strdup(full_name);
00532 item->fd = setup_socket(full_name);
00533 item->require_root = chk_pconf_root_pri(full_name);
00534 has_root_plugin |= item->require_root;
00535
00536 if (item->file_path && item->fd >= 0) {
00537 LIST_INSERT_HEAD((struct plugin_list *) p, item,
00538 pointers);
00539 info("Plugin %s added\n", full_name);
00540 } else {
00541
00542
00543 free(item);
00544 item = NULL;
00545 log_and_exit("strdup failed %s\n", full_name);
00546 }
00547 } else {
00548 log_and_exit("Memory allocation failure!\n");
00549 }
00550 }
00551 }
00552 }
00553 return 0;
00554 }
00555
00559 void child_cleanup(void)
00560 {
00561 int rc;
00562 int err;
00563
00564 do {
00565 siginfo_t si;
00566 memset(&si, 0, sizeof(siginfo_t));
00567
00568 rc = waitid(P_ALL, 0, &si, WNOHANG | WEXITED);
00569
00570 if (-1 == rc) {
00571 err = errno;
00572 if (err != ECHILD) {
00573 info("waitid %d - %s\n", err, strerror(err));
00574 }
00575 break;
00576 } else {
00577 if (0 == rc && si.si_pid == 0) {
00578 break;
00579 } else {
00580 if (si.si_code == CLD_EXITED && si.si_status != 0) {
00581 info("Plug-in process %d exited with %d\n", si.si_pid,
00582 si.si_status);
00583 }
00584 }
00585 }
00586 } while (1);
00587 }
00588
00592 void clean_up(void)
00593 {
00594 empty_plugin_list(&head);
00595 clean_sockets();
00596 }
00597
00602 int process_plugins(void)
00603 {
00604 clean_up();
00605 info("Scanning plug-in directory %s\n", plugin_dir);
00606 process_directory(plugin_dir, &head, process_plugin);
00607 if (allow_root_plugin == 1 && has_root_plugin == 0) {
00608 info("No plugin requires root privilege, dropping root privilege\n");
00609 flight_check();
00610 drop_privileges();
00611 }
00612 return 0;
00613 }
00614
00620 struct plugin *plugin_lookup(int fd)
00621 {
00622 struct plugin *plug = NULL;
00623 LIST_FOREACH(plug, &head, pointers) {
00624 if (plug->fd == fd) {
00625 return plug;
00626 }
00627 }
00628 return NULL;
00629 }
00630
00638 void exec_plugin(char *plugin, int client_fd, int require_root)
00639 {
00640 int err = 0;
00641
00642 info("Exec'ing plug-in = %s\n", plugin);
00643
00644 pid_t process = fork();
00645 if (process) {
00646
00647 int rc = close(client_fd);
00648 if (-1 == rc) {
00649 err = errno;
00650 info("Error on closing accepted socket in parent: %s\n",
00651 strerror(err));
00652 }
00653
00654 } else {
00655
00656 int exec_rc = 0;
00657 char fd_str[12];
00658 char *plugin_argv[7];
00659 extern char **environ;
00660 struct ucred cli_user_cred;
00661 socklen_t cli_user_cred_len = sizeof(cli_user_cred);
00662
00663
00664
00665
00666
00667 if (require_root == 0) {
00668 drop_privileges();
00669 } else {
00670 if (getuid()) {
00671 warn("Plugin %s require root privilege, but lsmd daemon "
00672 "is not run as root user\n", plugin);
00673 } else if (allow_root_plugin == 0) {
00674 warn("Plugin %s require root privilege, but %s disabled "
00675 "it globally\n", LSMD_CONF_FILE);
00676 drop_privileges();
00677 } else {
00678
00679 int rc_get_cli_uid =
00680 getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED,
00681 &cli_user_cred, &cli_user_cred_len);
00682 if (0 == rc_get_cli_uid) {
00683 if (cli_user_cred.uid != 0) {
00684 warn("Plugin %s require root privilege, but "
00685 "client is not run as root user\n", plugin);
00686 drop_privileges();
00687 } else {
00688 info("Plugin %s is running as root privilege\n",
00689 plugin);
00690 }
00691 } else {
00692 warn("Failed to get client socket uid, getsockopt() "
00693 "error: %d\n", errno);
00694 drop_privileges();
00695 }
00696 }
00697
00698 }
00699
00700
00701
00702 char *p_copy = strdup(plugin);
00703
00704 empty_plugin_list(&head);
00705 sprintf(fd_str, "%d", client_fd);
00706
00707 if (plugin_mem_debug) {
00708 char debug_out[64];
00709 snprintf(debug_out, (sizeof(debug_out) - 1),
00710 "--log-file=/tmp/leaking_%d-%d", getppid(), getpid());
00711
00712 plugin_argv[0] = "valgrind";
00713 plugin_argv[1] = "--leak-check=full";
00714 plugin_argv[2] = "--show-reachable=no";
00715 plugin_argv[3] = debug_out;
00716 plugin_argv[4] = p_copy;
00717 plugin_argv[5] = fd_str;
00718 plugin_argv[6] = NULL;
00719
00720 exec_rc = execve("/usr/bin/valgrind", plugin_argv, environ);
00721 } else {
00722 plugin_argv[0] = basename(p_copy);
00723 plugin_argv[1] = fd_str;
00724 plugin_argv[2] = NULL;
00725 exec_rc = execve(p_copy, plugin_argv, environ);
00726 }
00727
00728 if (-1 == exec_rc) {
00729 int err = errno;
00730 log_and_exit("Error on exec'ing Plugin %s: %s\n",
00731 p_copy, strerror(err));
00732 }
00733 }
00734 }
00735
00739 void _serving(void)
00740 {
00741 struct plugin *plug = NULL;
00742 struct timeval tmo;
00743 fd_set readfds;
00744 int nfds = 0;
00745 int err = 0;
00746
00747 process_plugins();
00748
00749 while (serve_state == RUNNING) {
00750 FD_ZERO(&readfds);
00751 nfds = 0;
00752
00753 tmo.tv_sec = 15;
00754 tmo.tv_usec = 0;
00755
00756 LIST_FOREACH(plug, &head, pointers) {
00757 nfds = max(plug->fd, nfds);
00758 FD_SET(plug->fd, &readfds);
00759 }
00760
00761 if (!nfds) {
00762 log_and_exit("No plugins found in directory %s\n", plugin_dir);
00763 }
00764
00765 nfds += 1;
00766 int ready = select(nfds, &readfds, NULL, NULL, &tmo);
00767
00768 if (-1 == ready) {
00769 if (serve_state != RUNNING) {
00770 return;
00771 } else {
00772 err = errno;
00773 log_and_exit("Error on selecting Plugin: %s", strerror(err));
00774 }
00775 } else if (ready > 0) {
00776 int fd = 0;
00777 for (fd = 0; fd < nfds; fd++) {
00778 if (FD_ISSET(fd, &readfds)) {
00779 int cfd = accept(fd, NULL, NULL);
00780 if (-1 != cfd) {
00781 struct plugin *p = plugin_lookup(fd);
00782 exec_plugin(p->file_path, cfd, p->require_root);
00783 } else {
00784 err = errno;
00785 info("Error on accepting request: %s", strerror(err));
00786 }
00787 }
00788 }
00789 }
00790 child_cleanup();
00791 }
00792 clean_up();
00793 }
00794
00798 void serve(void)
00799 {
00800 while (serve_state != EXIT) {
00801 if (serve_state == RESTART) {
00802 info("Reloading plug-ins\n");
00803 serve_state = RUNNING;
00804 }
00805 _serving();
00806 }
00807 clean_up();
00808 }
00809
00810 int main(int argc, char *argv[])
00811 {
00812 int c = 0;
00813
00814 LIST_INIT(&head);
00815
00816
00817 while (1) {
00818 static struct option l_options[] = {
00819 {"help", no_argument, 0, 'h'},
00820 {"plugindir", required_argument, 0, 0},
00821 {"socketdir", required_argument, 0, 0},
00822 {"confdir", required_argument, 0, 0},
00823 {0, 0, 0, 0}
00824 };
00825
00826 int option_index = 0;
00827 c = getopt_long(argc, argv, "hvd", l_options, &option_index);
00828
00829 if (c == -1) {
00830 break;
00831 }
00832
00833 switch (c) {
00834 case 0:
00835 switch (option_index) {
00836 case 1:
00837 plugin_dir = optarg;
00838 break;
00839 case 2:
00840 socket_dir = optarg;
00841 break;
00842 case 3:
00843 conf_dir = optarg;
00844 break;
00845 }
00846 break;
00847
00848 case 'h':
00849 usage();
00850 break;
00851
00852 case 'v':
00853 verbose_flag = 1;
00854 break;
00855
00856 case 'd':
00857 systemd = 1;
00858 break;
00859
00860 case '?':
00861 break;
00862
00863 default:
00864 abort();
00865 }
00866 }
00867
00868
00869 if (optind < argc) {
00870 printf("non-option ARGV-elements: ");
00871 while (optind < argc) {
00872 printf("%s \n", argv[optind++]);
00873 }
00874 printf("\n");
00875 exit(1);
00876 }
00877
00878
00879 if (!systemd) {
00880 openlog("lsmd", LOG_ODELAY, LOG_USER);
00881 }
00882
00883
00884 char *lsmd_conf_path = path_form(conf_dir, LSMD_CONF_FILE);
00885 parse_conf_bool(lsmd_conf_path, LSM_CONF_ALLOW_ROOT_OPT_NAME,
00886 &allow_root_plugin);
00887 free(lsmd_conf_path);
00888
00889
00890 if (getenv("LSM_VALGRIND")) {
00891 plugin_mem_debug = 1;
00892 }
00893
00894 install_sh();
00895 if (allow_root_plugin == 0) {
00896 drop_privileges();
00897 }
00898 flight_check();
00899
00900
00901 if (!systemd) {
00902 if (-1 == daemon(0, 0)) {
00903 int err = errno;
00904 log_and_exit("Error on calling daemon: %s\n", strerror(err));
00905 }
00906 }
00907
00908 serve();
00909 return EXIT_SUCCESS;
00910 }