|
Packit Service |
5956c7 |
/*
|
|
Packit Service |
5956c7 |
* Soft: Keepalived is a failover program for the LVS project
|
|
Packit Service |
5956c7 |
* <www.linuxvirtualserver.org>. It monitor & manipulate
|
|
Packit Service |
5956c7 |
* a loadbalanced server pool using multi-layer checks.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Part: Forked system call to launch an extra script.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Author: Alexandre Cassen, <acassen@linux-vs.org>
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
5956c7 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
5956c7 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
Packit Service |
5956c7 |
* See the GNU General Public License for more details.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* This program is free software; you can redistribute it and/or
|
|
Packit Service |
5956c7 |
* modify it under the terms of the GNU General Public License
|
|
Packit Service |
5956c7 |
* as published by the Free Software Foundation; either version
|
|
Packit Service |
5956c7 |
* 2 of the License, or (at your option) any later version.
|
|
Packit Service |
5956c7 |
*
|
|
Packit Service |
5956c7 |
* Copyright (C) 2001-2017 Alexandre Cassen, <acassen@gmail.com>
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#include "config.h"
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#include <unistd.h>
|
|
Packit Service |
5956c7 |
#include <stdlib.h>
|
|
Packit Service |
5956c7 |
#include <fcntl.h>
|
|
Packit Service |
5956c7 |
#include <errno.h>
|
|
Packit Service |
5956c7 |
#include <signal.h>
|
|
Packit Service |
5956c7 |
#include <grp.h>
|
|
Packit Service |
5956c7 |
#include <string.h>
|
|
Packit Service |
5956c7 |
#include <sys/stat.h>
|
|
Packit Service |
5956c7 |
#include <pwd.h>
|
|
Packit Service |
5956c7 |
#include <sys/resource.h>
|
|
Packit Service |
5956c7 |
#include <limits.h>
|
|
Packit Service |
5956c7 |
#include <sys/prctl.h>
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#include "notify.h"
|
|
Packit Service |
5956c7 |
#include "signals.h"
|
|
Packit Service |
5956c7 |
#include "logger.h"
|
|
Packit Service |
5956c7 |
#include "utils.h"
|
|
Packit Service |
5956c7 |
#include "process.h"
|
|
Packit Service |
5956c7 |
#include "parser.h"
|
|
Packit Service |
5956c7 |
#include "keepalived_magic.h"
|
|
Packit Service |
5956c7 |
#include "scheduler.h"
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Default user/group for script execution */
|
|
Packit Service |
5956c7 |
uid_t default_script_uid;
|
|
Packit Service |
5956c7 |
gid_t default_script_gid;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Have we got a default user OK? */
|
|
Packit Service |
5956c7 |
static bool default_script_uid_set = false;
|
|
Packit Service |
5956c7 |
static bool default_user_fail = false; /* Set if failed to set default user,
|
|
Packit Service |
5956c7 |
unless it defaults to root */
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Script security enabled */
|
|
Packit Service |
5956c7 |
bool script_security = false;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Buffer length needed for getpwnam_r/getgrname_r */
|
|
Packit Service |
5956c7 |
static size_t getpwnam_buf_len;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static char *path;
|
|
Packit Service |
5956c7 |
static bool path_is_malloced;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* The priority this process is running at */
|
|
Packit Service |
5956c7 |
static int cur_prio = INT_MAX;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Buffer for expanding notify script commands */
|
|
Packit Service |
5956c7 |
static char cmd_str_buf[MAXBUF];
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static bool
|
|
Packit Service |
5956c7 |
set_privileges(uid_t uid, gid_t gid)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int retval;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Ensure we receive SIGTERM if our parent process dies */
|
|
Packit Service |
5956c7 |
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If we have increased our priority, set it to default for the script */
|
|
Packit Service |
5956c7 |
if (cur_prio != INT_MAX)
|
|
Packit Service |
5956c7 |
cur_prio = getpriority(PRIO_PROCESS, 0);
|
|
Packit Service |
5956c7 |
if (cur_prio < 0)
|
|
Packit Service |
5956c7 |
setpriority(PRIO_PROCESS, 0, 0);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Drop our privileges if configured */
|
|
Packit Service |
5956c7 |
if (gid) {
|
|
Packit Service |
5956c7 |
retval = setgid(gid);
|
|
Packit Service |
5956c7 |
if (retval < 0) {
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't setgid: %d (%m)", gid);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Clear any extra supplementary groups */
|
|
Packit Service |
5956c7 |
retval = setgroups(1, &gid;;
|
|
Packit Service |
5956c7 |
if (retval < 0) {
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't setgroups: %d (%m)", gid);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (uid) {
|
|
Packit Service |
5956c7 |
retval = setuid(uid);
|
|
Packit Service |
5956c7 |
if (retval < 0) {
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't setuid: %d (%m)", uid);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Prepare for invoking process/script */
|
|
Packit Service |
5956c7 |
signal_handler_script();
|
|
Packit Service |
5956c7 |
set_std_fd(false);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
char *
|
|
Packit Service |
5956c7 |
cmd_str_r(const notify_script_t *script, char *buf, size_t len)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char *str_p;
|
|
Packit Service |
5956c7 |
int i;
|
|
Packit Service |
5956c7 |
size_t str_len;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
str_p = buf;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
for (i = 0; i < script->num_args; i++) {
|
|
Packit Service |
5956c7 |
/* Check there is enough room for the next word */
|
|
Packit Service |
5956c7 |
str_len = strlen(script->args[i]);
|
|
Packit Service |
5956c7 |
if (str_p + str_len + 2 + (i ? 1 : 0) >= buf + len)
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (i)
|
|
Packit Service |
5956c7 |
*str_p++ = ' ';
|
|
Packit Service |
93d34c |
*str_p++ = '\'';
|
|
Packit Service |
5956c7 |
strcpy(str_p, script->args[i]);
|
|
Packit Service |
5956c7 |
str_p += str_len;
|
|
Packit Service |
93d34c |
*str_p++ = '\'';
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
*str_p = '\0';
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return buf;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
char *
|
|
Packit Service |
5956c7 |
cmd_str(const notify_script_t *script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
size_t len;
|
|
Packit Service |
5956c7 |
int i;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
for (i = 0, len = 0; i < script->num_args; i++)
|
|
Packit Service |
5956c7 |
len += strlen(script->args[i]) + 3; /* Add two ', and trailing space (or null for last arg) */
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (len > sizeof cmd_str_buf)
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return cmd_str_r(script, cmd_str_buf, sizeof cmd_str_buf);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Execute external script/program to process FIFO */
|
|
Packit Service |
5956c7 |
static pid_t
|
|
Packit Service |
5956c7 |
notify_fifo_exec(thread_master_t *m, int (*func) (thread_t *), void *arg, notify_script_t *script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
pid_t pid;
|
|
Packit Service |
5956c7 |
int retval;
|
|
Packit Service |
5956c7 |
char *scr;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
pid = local_fork();
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* In case of fork is error. */
|
|
Packit Service |
5956c7 |
if (pid < 0) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Failed fork process");
|
|
Packit Service |
5956c7 |
return -1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* In case of this is parent process */
|
|
Packit Service |
5956c7 |
if (pid) {
|
|
Packit Service |
5956c7 |
thread_add_child(m, func, arg, pid, TIMER_NEVER);
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#ifdef _MEM_CHECK_
|
|
Packit Service |
5956c7 |
skip_mem_dump();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
setpgid(0, 0);
|
|
Packit Service |
5956c7 |
set_privileges(script->uid, script->gid);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (script->flags | SC_EXECABLE) {
|
|
Packit Service |
5956c7 |
/* If keepalived dies, we want the script to die */
|
|
Packit Service |
5956c7 |
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
execve(script->args[0], script->args, environ);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (errno == EACCES)
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "FIFO notify script %s is not executable", script->args[0]);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to execute FIFO notify script %s - errno %d - %m", script->args[0], errno);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
retval = system(scr = cmd_str(script));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (retval == 127) {
|
|
Packit Service |
5956c7 |
/* couldn't exec command */
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't exec FIFO command: %s", scr);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else if (retval == -1)
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Error exec-ing FIFO command: %s", scr);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* unreached unless error */
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static void
|
|
Packit Service |
5956c7 |
fifo_open(notify_fifo_t* fifo, int (*script_exit)(thread_t *), const char *type)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int ret;
|
|
Packit Service |
5956c7 |
int sav_errno;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (fifo->name) {
|
|
Packit Service |
5956c7 |
sav_errno = 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!(ret = mkfifo(fifo->name, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)))
|
|
Packit Service |
5956c7 |
fifo->created_fifo = true;
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
sav_errno = errno;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (sav_errno != EEXIST)
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to create %snotify fifo %s", type, fifo->name);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!sav_errno || sav_errno == EEXIST) {
|
|
Packit Service |
5956c7 |
/* Run the notify script if there is one */
|
|
Packit Service |
5956c7 |
if (fifo->script)
|
|
Packit Service |
5956c7 |
notify_fifo_exec(master, script_exit, fifo, fifo->script);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Now open the fifo */
|
|
Packit Service |
5956c7 |
if ((fifo->fd = open(fifo->name, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOFOLLOW)) == -1) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to open %snotify fifo %s - errno %d", type, fifo->name, errno);
|
|
Packit Service |
5956c7 |
if (fifo->created_fifo) {
|
|
Packit Service |
5956c7 |
unlink(fifo->name);
|
|
Packit Service |
5956c7 |
fifo->created_fifo = false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (fifo->fd == -1) {
|
|
Packit Service |
5956c7 |
FREE(fifo->name);
|
|
Packit Service |
5956c7 |
fifo->name = NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
notify_fifo_open(notify_fifo_t* global_fifo, notify_fifo_t* fifo, int (*script_exit)(thread_t *), const char *type)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
/* Open the global FIFO if specified */
|
|
Packit Service |
5956c7 |
if (global_fifo->name)
|
|
Packit Service |
5956c7 |
fifo_open(global_fifo, script_exit, "");
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Now the specific FIFO */
|
|
Packit Service |
5956c7 |
if (fifo->name)
|
|
Packit Service |
5956c7 |
fifo_open(fifo, script_exit, type);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static void
|
|
Packit Service |
5956c7 |
fifo_close(notify_fifo_t* fifo)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
if (fifo->fd != -1) {
|
|
Packit Service |
5956c7 |
close(fifo->fd);
|
|
Packit Service |
5956c7 |
fifo->fd = -1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
if (fifo->created_fifo)
|
|
Packit Service |
5956c7 |
unlink(fifo->name);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
notify_fifo_close(notify_fifo_t* global_fifo, notify_fifo_t* fifo)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
if (global_fifo->fd != -1)
|
|
Packit Service |
5956c7 |
fifo_close(global_fifo);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
fifo_close(fifo);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* perform a system call */
|
|
Packit Service |
5956c7 |
static void system_call(const notify_script_t *) __attribute__ ((noreturn));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static void
|
|
Packit Service |
5956c7 |
system_call(const notify_script_t* script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char *command_line = NULL;
|
|
Packit Service |
5956c7 |
char *str;
|
|
Packit Service |
5956c7 |
int retval;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (set_privileges(script->uid, script->gid))
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Move us into our own process group, so if the script needs to be killed
|
|
Packit Service |
5956c7 |
* all its child processes will also be killed. */
|
|
Packit Service |
5956c7 |
setpgid(0, 0);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (script->flags & SC_EXECABLE) {
|
|
Packit Service |
5956c7 |
/* If keepalived dies, we want the script to die */
|
|
Packit Service |
5956c7 |
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
execve(script->args[0], script->args, environ);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* error */
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Error exec-ing command '%s', error %d: %m", script->args[0], errno);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
retval = system(str = cmd_str(script));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (retval == -1)
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Error exec-ing command: %s", str);
|
|
Packit Service |
5956c7 |
else if (WIFEXITED(retval)) {
|
|
Packit Service |
5956c7 |
if (WEXITSTATUS(retval) == 127) {
|
|
Packit Service |
5956c7 |
/* couldn't find command */
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't find command: %s", str);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else if (WEXITSTATUS(retval) == 126) {
|
|
Packit Service |
5956c7 |
/* couldn't find command */
|
|
Packit Service |
5956c7 |
log_message(LOG_ALERT, "Couldn't execute command: %s", str);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (command_line)
|
|
Packit Service |
5956c7 |
FREE(command_line);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (retval == -1 ||
|
|
Packit Service |
5956c7 |
(WIFEXITED(retval) && (WEXITSTATUS(retval) == 126 || WEXITSTATUS(retval) == 127)))
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
if (WIFEXITED(retval))
|
|
Packit Service |
5956c7 |
exit(WEXITSTATUS(retval));
|
|
Packit Service |
5956c7 |
if (WIFSIGNALED(retval))
|
|
Packit Service |
5956c7 |
kill(getpid(), WTERMSIG(retval));
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Execute external script/program */
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
notify_exec(const notify_script_t *script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
pid_t pid;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#ifdef ENABLE_LOG_TO_FILE
|
|
Packit Service |
5956c7 |
if (log_file_name)
|
|
Packit Service |
5956c7 |
flush_log_file();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
pid = local_fork();
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (pid < 0) {
|
|
Packit Service |
5956c7 |
/* fork error */
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Failed fork process");
|
|
Packit Service |
5956c7 |
return -1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (pid) {
|
|
Packit Service |
5956c7 |
/* parent process */
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#ifdef _MEM_CHECK_
|
|
Packit Service |
5956c7 |
skip_mem_dump();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
system_call(script);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* We should never get here */
|
|
Packit Service |
5956c7 |
exit(0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
system_call_script(thread_master_t *m, int (*func) (thread_t *), void * arg, unsigned long timer, notify_script_t* script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
pid_t pid;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Daemonization to not degrade our scheduling timer */
|
|
Packit Service |
5956c7 |
#ifdef ENABLE_LOG_TO_FILE
|
|
Packit Service |
5956c7 |
if (log_file_name)
|
|
Packit Service |
5956c7 |
flush_log_file();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
pid = local_fork();
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (pid < 0) {
|
|
Packit Service |
5956c7 |
/* fork error */
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Failed fork process");
|
|
Packit Service |
5956c7 |
return -1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (pid) {
|
|
Packit Service |
5956c7 |
/* parent process */
|
|
Packit Service |
5956c7 |
thread_add_child(m, func, arg, pid, timer);
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Child process */
|
|
Packit Service |
5956c7 |
#ifdef _MEM_CHECK_
|
|
Packit Service |
5956c7 |
skip_mem_dump();
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
system_call(script);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
exit(0); /* Script errors aren't server errors */
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
child_killed_thread(thread_t *thread)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
thread_master_t *m = thread->master;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If the child didn't die, then force it */
|
|
Packit Service |
5956c7 |
if (thread->type == THREAD_CHILD_TIMEOUT)
|
|
Packit Service |
5956c7 |
kill(-getpgid(thread->u.c.pid), SIGKILL);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If all children have died, we can now complete the
|
|
Packit Service |
5956c7 |
* termination process */
|
|
Packit Service |
5956c7 |
if (!&m->child.rb_root.rb_node && !m->shutdown_timer_running)
|
|
Packit Service |
5956c7 |
thread_add_terminate_event(m);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
script_killall(thread_master_t *m, int signo, bool requeue)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
thread_t *thread;
|
|
Packit Service |
5956c7 |
pid_t p_pgid, c_pgid;
|
|
Packit Service |
5956c7 |
#ifndef HAVE_SIGNALFD
|
|
Packit Service |
5956c7 |
sigset_t old_set, child_wait;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
sigmask_func(0, NULL, &old_set);
|
|
Packit Service |
5956c7 |
if (!sigismember(&old_set, SIGCHLD)) {
|
|
Packit Service |
5956c7 |
sigemptyset(&child_wait);
|
|
Packit Service |
5956c7 |
sigaddset(&child_wait, SIGCHLD);
|
|
Packit Service |
5956c7 |
sigmask_func(SIG_BLOCK, &child_wait, NULL);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
p_pgid = getpgid(0);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
rb_for_each_entry_cached(thread, &m->child, n) {
|
|
Packit Service |
5956c7 |
c_pgid = getpgid(thread->u.c.pid);
|
|
Packit Service |
5956c7 |
if (c_pgid != p_pgid)
|
|
Packit Service |
5956c7 |
kill(-c_pgid, signo);
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Child process %d in our process group %d", c_pgid, p_pgid);
|
|
Packit Service |
5956c7 |
kill(thread->u.c.pid, signo);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* We want to timeout the killed children in 1 second */
|
|
Packit Service |
5956c7 |
if (requeue && signo != SIGKILL)
|
|
Packit Service |
5956c7 |
thread_children_reschedule(m, child_killed_thread, TIMER_HZ);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#ifndef HAVE_SIGNALFD
|
|
Packit Service |
5956c7 |
if (!sigismember(&old_set, SIGCHLD))
|
|
Packit Service |
5956c7 |
sigmask_func(SIG_UNBLOCK, &child_wait, NULL);
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static bool
|
|
Packit Service |
5956c7 |
is_executable(struct stat *buf, uid_t uid, gid_t gid)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
return (uid == 0 && buf->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ||
|
|
Packit Service |
5956c7 |
(uid == buf->st_uid && buf->st_mode & S_IXUSR) ||
|
|
Packit Service |
5956c7 |
(uid != buf->st_uid &&
|
|
Packit Service |
5956c7 |
((gid == buf->st_gid && buf->st_mode & S_IXGRP) ||
|
|
Packit Service |
5956c7 |
(gid != buf->st_gid && buf->st_mode & S_IXOTH)));
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static void
|
|
Packit Service |
5956c7 |
replace_cmd_name(notify_script_t *script, char *new_path)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
size_t len;
|
|
Packit Service |
5956c7 |
char **wp = &script->args[1];
|
|
Packit Service |
5956c7 |
size_t num_words = 1;
|
|
Packit Service |
5956c7 |
char **params;
|
|
Packit Service |
5956c7 |
char **word_ptrs;
|
|
Packit Service |
5956c7 |
char *words;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
len = strlen(new_path) + 1;
|
|
Packit Service |
5956c7 |
while (*wp)
|
|
Packit Service |
5956c7 |
len += strlen(*wp++) + 1;
|
|
Packit Service |
5956c7 |
num_words = ((char **)script->args[0] - &script->args[0]) - 1;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
params = word_ptrs = MALLOC((num_words + 1) * sizeof(char *) + len);
|
|
Packit Service |
5956c7 |
words = (char *)¶ms[num_words + 1];
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
strcpy(words, new_path);
|
|
Packit Service |
5956c7 |
*(word_ptrs++) = words;
|
|
Packit Service |
5956c7 |
words += strlen(words) + 1;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
wp = &script->args[1];
|
|
Packit Service |
5956c7 |
while (*wp) {
|
|
Packit Service |
5956c7 |
strcpy(words, *wp);
|
|
Packit Service |
5956c7 |
*(word_ptrs++) = words;
|
|
Packit Service |
5956c7 |
words += strlen(*wp) + 1;
|
|
Packit Service |
5956c7 |
wp++;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
*word_ptrs = NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
FREE(script->args);
|
|
Packit Service |
5956c7 |
script->args = params;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* The following function is essentially __execve() from glibc */
|
|
Packit Service |
5956c7 |
static int
|
|
Packit Service |
5956c7 |
find_path(notify_script_t *script)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
size_t filename_len;
|
|
Packit Service |
5956c7 |
size_t file_len;
|
|
Packit Service |
5956c7 |
size_t path_len;
|
|
Packit Service |
5956c7 |
char *file = script->args[0];
|
|
Packit Service |
5956c7 |
struct stat buf;
|
|
Packit Service |
5956c7 |
int ret;
|
|
Packit Service |
5956c7 |
int ret_val = ENOENT;
|
|
Packit Service |
5956c7 |
int sgid_num;
|
|
Packit Service |
5956c7 |
gid_t *sgid_list = NULL;
|
|
Packit Service |
5956c7 |
const char *subp;
|
|
Packit Service |
5956c7 |
bool got_eacces = false;
|
|
Packit Service |
5956c7 |
const char *p;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* We check the simple case first. */
|
|
Packit Service |
5956c7 |
if (*file == '\0')
|
|
Packit Service |
5956c7 |
return ENOENT;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
filename_len = strlen(file);
|
|
Packit Service |
5956c7 |
if (filename_len >= PATH_MAX) {
|
|
Packit Service |
5956c7 |
ret_val = ENAMETOOLONG;
|
|
Packit Service |
5956c7 |
goto exit1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Don't search when it contains a slash. */
|
|
Packit Service |
5956c7 |
if (strchr (file, '/') != NULL) {
|
|
Packit Service |
5956c7 |
ret_val = 0;
|
|
Packit Service |
5956c7 |
goto exit1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Get the path if we haven't already done so, and if that doesn't
|
|
Packit Service |
5956c7 |
* exist, use CS_PATH */
|
|
Packit Service |
5956c7 |
if (!path) {
|
|
Packit Service |
5956c7 |
path = getenv ("PATH");
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!path) {
|
|
Packit Service |
5956c7 |
size_t cs_path_len;
|
|
Packit Service |
5956c7 |
path = MALLOC(cs_path_len = confstr(_CS_PATH, NULL, 0));
|
|
Packit Service |
5956c7 |
confstr(_CS_PATH, path, cs_path_len);
|
|
Packit Service |
5956c7 |
path_is_malloced = true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Although GLIBC does not enforce NAME_MAX, we set it as the maximum
|
|
Packit Service |
5956c7 |
size to avoid unbounded stack allocation. Same applies for
|
|
Packit Service |
5956c7 |
PATH_MAX. */
|
|
Packit Service |
5956c7 |
file_len = strnlen (file, NAME_MAX + 1);
|
|
Packit Service |
5956c7 |
path_len = strnlen (path, PATH_MAX - 1) + 1;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (file_len > NAME_MAX) {
|
|
Packit Service |
5956c7 |
ret_val = ENAMETOOLONG;
|
|
Packit Service |
5956c7 |
goto exit1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Set file access to the relevant uid/gid */
|
|
Packit Service |
5956c7 |
if (script->gid) {
|
|
Packit Service |
5956c7 |
if (setegid(script->gid)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to set egid to %d (%m)", script->gid);
|
|
Packit Service |
5956c7 |
ret_val = EACCES;
|
|
Packit Service |
5956c7 |
goto exit1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Get our supplementary groups */
|
|
Packit Service |
5956c7 |
sgid_num = getgroups(0, NULL);
|
|
Packit Service |
5956c7 |
sgid_list = MALLOC(((size_t)sgid_num + 1) * sizeof(gid_t));
|
|
Packit Service |
5956c7 |
sgid_num = getgroups(sgid_num, sgid_list);
|
|
Packit Service |
5956c7 |
sgid_list[sgid_num++] = 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Clear the supplementary group list */
|
|
Packit Service |
5956c7 |
if (setgroups(1, &script->gid)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to set supplementary gids (%m)");
|
|
Packit Service |
5956c7 |
ret_val = EACCES;
|
|
Packit Service |
5956c7 |
goto exit;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
if (script->uid && seteuid(script->uid)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to set euid to %d (%m)", script->uid);
|
|
Packit Service |
5956c7 |
ret_val = EACCES;
|
|
Packit Service |
5956c7 |
goto exit;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
for (p = path; ; p = subp)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char buffer[path_len + file_len + 1];
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
subp = strchrnul (p, ':');
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* PATH is larger than PATH_MAX and thus potentially larger than
|
|
Packit Service |
5956c7 |
the stack allocation. */
|
|
Packit Service |
5956c7 |
if (subp >= p + path_len) {
|
|
Packit Service |
5956c7 |
/* There are no more paths, bail out. */
|
|
Packit Service |
5956c7 |
if (*subp == '\0') {
|
|
Packit Service |
5956c7 |
ret_val = ENOENT;
|
|
Packit Service |
5956c7 |
goto exit;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Otherwise skip to next one. */
|
|
Packit Service |
5956c7 |
continue;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Use the current path entry, plus a '/' if nonempty, plus the file to execute. */
|
|
Packit Service |
5956c7 |
char *pend = mempcpy (buffer, p, (size_t)(subp - p));
|
|
Packit Service |
5956c7 |
*pend = '/';
|
|
Packit Service |
5956c7 |
memcpy (pend + (p < subp), file, file_len + 1);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
ret = stat (buffer, &buf;;
|
|
Packit Service |
5956c7 |
if (!ret) {
|
|
Packit Service |
5956c7 |
if (!S_ISREG(buf.st_mode))
|
|
Packit Service |
5956c7 |
errno = EACCES;
|
|
Packit Service |
5956c7 |
else if (!is_executable(&buf, script->uid, script->gid)) {
|
|
Packit Service |
5956c7 |
errno = EACCES;
|
|
Packit Service |
5956c7 |
} else {
|
|
Packit Service |
5956c7 |
/* Success */
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "WARNING - script `%s` resolved by path search to `%s`. Please specify full path.", script->args[0], buffer);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Copy the found file name, and any parameters */
|
|
Packit Service |
5956c7 |
replace_cmd_name(script, buffer);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
ret_val = 0;
|
|
Packit Service |
5956c7 |
got_eacces = false;
|
|
Packit Service |
5956c7 |
goto exit;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
switch (errno)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
case ENOEXEC:
|
|
Packit Service |
5956c7 |
case EACCES:
|
|
Packit Service |
5956c7 |
/* Record that we got a 'Permission denied' error. If we end
|
|
Packit Service |
5956c7 |
up finding no executable we can use, we want to diagnose
|
|
Packit Service |
5956c7 |
that we did find one but were denied access. */
|
|
Packit Service |
5956c7 |
if (!ret)
|
|
Packit Service |
5956c7 |
got_eacces = true;
|
|
Packit Service |
5956c7 |
case ENOENT:
|
|
Packit Service |
5956c7 |
case ESTALE:
|
|
Packit Service |
5956c7 |
case ENOTDIR:
|
|
Packit Service |
5956c7 |
/* Those errors indicate the file is missing or not executable
|
|
Packit Service |
5956c7 |
by us, in which case we want to just try the next path
|
|
Packit Service |
5956c7 |
directory. */
|
|
Packit Service |
5956c7 |
case ENODEV:
|
|
Packit Service |
5956c7 |
case ETIMEDOUT:
|
|
Packit Service |
5956c7 |
/* Some strange filesystems like AFS return even
|
|
Packit Service |
5956c7 |
stranger error numbers. They cannot reasonably mean
|
|
Packit Service |
5956c7 |
anything else so ignore those, too. */
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
default:
|
|
Packit Service |
5956c7 |
/* Some other error means we found an executable file, but
|
|
Packit Service |
5956c7 |
something went wrong accessing it; return the error to our
|
|
Packit Service |
5956c7 |
caller. */
|
|
Packit Service |
5956c7 |
ret_val = -1;
|
|
Packit Service |
5956c7 |
goto exit;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (*subp++ == '\0')
|
|
Packit Service |
5956c7 |
break;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
exit:
|
|
Packit Service |
5956c7 |
/* Restore root euid/egid */
|
|
Packit Service |
5956c7 |
if (script->uid && seteuid(0))
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to restore euid after script search (%m)");
|
|
Packit Service |
5956c7 |
if (script->gid) {
|
|
Packit Service |
5956c7 |
if (setegid(0))
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to restore egid after script search (%m)");
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* restore supplementary groups */
|
|
Packit Service |
5956c7 |
if (sgid_list) {
|
|
Packit Service |
5956c7 |
if (setgroups((size_t)sgid_num, sgid_list))
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to restore supplementary groups after script search (%m)");
|
|
Packit Service |
5956c7 |
FREE(sgid_list);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
exit1:
|
|
Packit Service |
5956c7 |
/* We tried every element and none of them worked. */
|
|
Packit Service |
5956c7 |
if (got_eacces) {
|
|
Packit Service |
5956c7 |
/* At least one failure was due to permissions, so report that error. */
|
|
Packit Service |
5956c7 |
return EACCES;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return ret_val;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static int
|
|
Packit Service |
5956c7 |
check_security(char *filename, bool script_security)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char *next;
|
|
Packit Service |
5956c7 |
char *slash;
|
|
Packit Service |
5956c7 |
char sav;
|
|
Packit Service |
5956c7 |
int ret;
|
|
Packit Service |
5956c7 |
struct stat buf;
|
|
Packit Service |
5956c7 |
int flags = 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
next = filename;
|
|
Packit Service |
5956c7 |
while (next) {
|
|
Packit Service |
5956c7 |
slash = strchrnul(next, '/');
|
|
Packit Service |
5956c7 |
if (*slash)
|
|
Packit Service |
5956c7 |
next = slash + 1;
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
slash = NULL;
|
|
Packit Service |
5956c7 |
next = NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (slash) {
|
|
Packit Service |
5956c7 |
/* We want to check '/' for first time around */
|
|
Packit Service |
5956c7 |
if (slash == filename)
|
|
Packit Service |
5956c7 |
slash++;
|
|
Packit Service |
5956c7 |
sav = *slash;
|
|
Packit Service |
5956c7 |
*slash = 0;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
ret = fstatat(0, filename, &buf, AT_SYMLINK_NOFOLLOW);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Restore the full path name */
|
|
Packit Service |
5956c7 |
if (slash)
|
|
Packit Service |
5956c7 |
*slash = sav;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (ret) {
|
|
Packit Service |
5956c7 |
if (errno == EACCES || errno == ELOOP || errno == ENOENT || errno == ENOTDIR)
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "check_script_secure could not find script '%s' - disabling", filename);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "check_script_secure('%s') returned errno %d - %s - disabling", filename, errno, strerror(errno));
|
|
Packit Service |
5956c7 |
return flags | SC_NOTFOUND;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If it is not the last item, it must be a directory. If it is the last item
|
|
Packit Service |
5956c7 |
* it must be a file or a symbolic link. */
|
|
Packit Service |
5956c7 |
if ((slash && !S_ISDIR(buf.st_mode)) ||
|
|
Packit Service |
5956c7 |
(!slash &&
|
|
Packit Service |
5956c7 |
!S_ISREG(buf.st_mode) &&
|
|
Packit Service |
5956c7 |
!S_ISLNK(buf.st_mode))) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Wrong file type found in script path '%s'.", filename);
|
|
Packit Service |
5956c7 |
return flags | SC_INHIBIT;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (buf.st_uid || /* Owner is not root */
|
|
Packit Service |
5956c7 |
(((S_ISDIR(buf.st_mode) && /* A directory without the sticky bit set */
|
|
Packit Service |
5956c7 |
!(buf.st_mode & S_ISVTX)) ||
|
|
Packit Service |
5956c7 |
S_ISREG(buf.st_mode)) && /* This is a file */
|
|
Packit Service |
5956c7 |
((buf.st_gid && buf.st_mode & S_IWGRP) || /* Group is not root and group write permission */
|
|
Packit Service |
5956c7 |
buf.st_mode & S_IWOTH))) { /* World has write permission */
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unsafe permissions found for script '%s'%s.", filename, script_security ? " - disabling" : "");
|
|
Packit Service |
5956c7 |
flags |= SC_INSECURE;
|
|
Packit Service |
5956c7 |
return flags | (script_security ? SC_INHIBIT : 0);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return flags;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
check_script_secure(notify_script_t *script,
|
|
Packit Service |
5956c7 |
#ifndef _HAVE_LIBMAGIC_
|
|
Packit Service |
5956c7 |
__attribute__((unused))
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
magic_t magic)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int flags;
|
|
Packit Service |
5956c7 |
int ret, ret_real, ret_new;
|
|
Packit Service |
5956c7 |
struct stat file_buf, real_buf;
|
|
Packit Service |
5956c7 |
bool need_script_protection = false;
|
|
Packit Service |
5956c7 |
uid_t old_uid = 0;
|
|
Packit Service |
5956c7 |
gid_t old_gid = 0;
|
|
Packit Service |
5956c7 |
char *new_path;
|
|
Packit Service |
5956c7 |
int sav_errno;
|
|
Packit Service |
5956c7 |
char *real_file_path;
|
|
Packit Service |
5956c7 |
char *orig_file_part, *new_file_part;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!script)
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If the script starts "</" (possibly with white space between
|
|
Packit Service |
5956c7 |
* the '<' and '/'), it is checking for a file being openable,
|
|
Packit Service |
5956c7 |
* so it won't be executed */
|
|
Packit Service |
5956c7 |
if (script->args[0][0] == '<' &&
|
|
Packit Service |
5956c7 |
script->args[0][strspn(script->args[0] + 1, " \t") + 1] == '/')
|
|
Packit Service |
5956c7 |
return SC_SYSTEM;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!strchr(script->args[0], '/')) {
|
|
Packit Service |
5956c7 |
/* It is a bare file name, so do a path search */
|
|
Packit Service |
5956c7 |
if ((ret = find_path(script))) {
|
|
Packit Service |
5956c7 |
if (ret == EACCES)
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Permissions failure for script %s in path - disabling", script->args[0]);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Cannot find script %s in path - disabling", script->args[0]);
|
|
Packit Service |
5956c7 |
return SC_NOTFOUND;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Check script accessible by the user running it */
|
|
Packit Service |
5956c7 |
if (script->uid)
|
|
Packit Service |
5956c7 |
old_uid = geteuid();
|
|
Packit Service |
5956c7 |
if (script->gid)
|
|
Packit Service |
5956c7 |
old_gid = getegid();
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if ((script->gid && setegid(script->gid)) ||
|
|
Packit Service |
5956c7 |
(script->uid && seteuid(script->uid))) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to set uid:gid %d:%d for script %s - disabling", script->uid, script->gid, script->args[0]);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if ((script->uid && seteuid(old_uid)) ||
|
|
Packit Service |
5956c7 |
(script->gid && setegid(old_gid)))
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to restore uid:gid %d:%d after script %s", script->uid, script->gid, script->args[0]);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return SC_INHIBIT;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Remove /./, /../, multiple /'s, and resolve symbolic links */
|
|
Packit Service |
5956c7 |
new_path = realpath(script->args[0], NULL);
|
|
Packit Service |
5956c7 |
sav_errno = errno;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if ((script->gid && setegid(old_gid)) ||
|
|
Packit Service |
5956c7 |
(script->uid && seteuid(old_uid)))
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to restore uid:gid %d:%d after checking script %s", script->uid, script->gid, script->args[0]);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!new_path)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Script %s cannot be accessed - %s", script->args[0], strerror(sav_errno));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return SC_NOTFOUND;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
real_file_path = NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
orig_file_part = strrchr(script->args[0], '/');
|
|
Packit Service |
5956c7 |
new_file_part = strrchr(new_path, '/');
|
|
Packit Service |
5956c7 |
if (strcmp(script->args[0], new_path)) {
|
|
Packit Service |
5956c7 |
/* The path name is different */
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* If the file name parts don't match, we need to be careful to
|
|
Packit Service |
5956c7 |
* ensure that we preserve the file name part since some executables
|
|
Packit Service |
5956c7 |
* alter their behaviour based on what they are called */
|
|
Packit Service |
5956c7 |
if (strcmp(orig_file_part + 1, new_file_part + 1)) {
|
|
Packit Service |
5956c7 |
real_file_path = new_path;
|
|
Packit Service |
5956c7 |
new_path = MALLOC(new_file_part - real_file_path + 1 + strlen(orig_file_part + 1) + 1);
|
|
Packit Service |
5956c7 |
strncpy(new_path, real_file_path, new_file_part + 1 - real_file_path);
|
|
Packit Service |
5956c7 |
strcpy(new_path + (new_file_part + 1 - real_file_path), orig_file_part + 1);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Now check this is the same file */
|
|
Packit Service |
5956c7 |
ret_real = stat(real_file_path, &real_buf);
|
|
Packit Service |
5956c7 |
ret_new = stat(new_path, &file_buf);
|
|
Packit Service |
5956c7 |
if (!ret_real &&
|
|
Packit Service |
5956c7 |
(ret_new ||
|
|
Packit Service |
5956c7 |
real_buf.st_dev != file_buf.st_dev ||
|
|
Packit Service |
5956c7 |
real_buf.st_ino != file_buf.st_ino)) {
|
|
Packit Service |
5956c7 |
/* It doesn't resolve to the same file */
|
|
Packit Service |
5956c7 |
FREE(new_path);
|
|
Packit Service |
5956c7 |
new_path = real_file_path;
|
|
Packit Service |
5956c7 |
real_file_path = NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (strcmp(script->args[0], new_path)) {
|
|
Packit Service |
5956c7 |
/* We need to set up all the args again */
|
|
Packit Service |
5956c7 |
replace_cmd_name(script, new_path);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!real_file_path)
|
|
Packit Service |
5956c7 |
free(new_path);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
FREE(new_path);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Get the permissions for the file itself */
|
|
Packit Service |
5956c7 |
if (stat(real_file_path ? real_file_path : script->args[0], &file_buf)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to access script `%s` - disabling", script->args[0]);
|
|
Packit Service |
5956c7 |
return SC_NOTFOUND;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
flags = SC_ISSCRIPT;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* We have the final file. Check if root is executing it, or it is set uid/gid root. */
|
|
Packit Service |
5956c7 |
if (is_executable(&file_buf, script->uid, script->gid)) {
|
|
Packit Service |
5956c7 |
flags |= SC_EXECUTABLE;
|
|
Packit Service |
5956c7 |
if (script->uid == 0 || script->gid == 0 ||
|
|
Packit Service |
5956c7 |
(file_buf.st_uid == 0 && (file_buf.st_mode & S_IXUSR) && (file_buf.st_mode & S_ISUID)) ||
|
|
Packit Service |
5956c7 |
(file_buf.st_gid == 0 && (file_buf.st_mode & S_IXGRP) && (file_buf.st_mode & S_ISGID)))
|
|
Packit Service |
5956c7 |
need_script_protection = true;
|
|
Packit Service |
5956c7 |
} else
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "WARNING - script '%s' is not executable for uid:gid %d:%d - disabling.", script->args[0], script->uid, script->gid);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Default to execable */
|
|
Packit Service |
5956c7 |
script->flags |= SC_EXECABLE;
|
|
Packit Service |
5956c7 |
#ifdef _HAVE_LIBMAGIC_
|
|
Packit Service |
5956c7 |
if (magic && flags & SC_EXECUTABLE) {
|
|
Packit Service |
5956c7 |
const char *magic_desc = magic_file(magic, real_file_path ? real_file_path : script->args[0]);
|
|
Packit Service |
5956c7 |
if (!strstr(magic_desc, " executable") &&
|
|
Packit Service |
5956c7 |
!strstr(magic_desc, " shared object")) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Please add a #! shebang to script %s", script->args[0]);
|
|
Packit Service |
5956c7 |
script->flags &= ~SC_EXECABLE;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
#endif
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!need_script_protection) {
|
|
Packit Service |
5956c7 |
if (real_file_path)
|
|
Packit Service |
5956c7 |
free(real_file_path);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return flags;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Make sure that all parts of the path are not non-root writable */
|
|
Packit Service |
5956c7 |
flags |= check_security(script->args[0], script_security);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (real_file_path) {
|
|
Packit Service |
5956c7 |
flags |= check_security(real_file_path, script_security);
|
|
Packit Service |
5956c7 |
free(real_file_path);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return flags;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
int
|
|
Packit Service |
5956c7 |
check_notify_script_secure(notify_script_t **script_p, magic_t magic)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int flags;
|
|
Packit Service |
5956c7 |
notify_script_t *script = *script_p;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!script)
|
|
Packit Service |
5956c7 |
return 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
flags = check_script_secure(script, magic);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Mark not to run if needs inhibiting */
|
|
Packit Service |
5956c7 |
if ((flags & (SC_INHIBIT | SC_NOTFOUND)) ||
|
|
Packit Service |
5956c7 |
!(flags & SC_EXECUTABLE))
|
|
Packit Service |
5956c7 |
free_notify_script(script_p);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return flags;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
static void
|
|
Packit Service |
5956c7 |
set_pwnam_buf_len(void)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
long buf_len;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Get buffer length needed for getpwnam_r/getgrnam_r */
|
|
Packit Service |
5956c7 |
if ((buf_len = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
|
|
Packit Service |
5956c7 |
getpwnam_buf_len = 1024; /* A safe default if no value is returned */
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
getpwnam_buf_len = (size_t)buf_len;
|
|
Packit Service |
5956c7 |
if ((buf_len = sysconf(_SC_GETGR_R_SIZE_MAX)) != -1 &&
|
|
Packit Service |
5956c7 |
(size_t)buf_len > getpwnam_buf_len)
|
|
Packit Service |
5956c7 |
getpwnam_buf_len = (size_t)buf_len;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
bool
|
|
Packit Service |
5956c7 |
set_uid_gid(const char *username, const char *groupname, uid_t *uid_p, gid_t *gid_p, bool default_user)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
uid_t uid;
|
|
Packit Service |
5956c7 |
gid_t gid;
|
|
Packit Service |
5956c7 |
struct passwd pwd;
|
|
Packit Service |
5956c7 |
struct passwd *pwd_p;
|
|
Packit Service |
5956c7 |
struct group grp;
|
|
Packit Service |
5956c7 |
struct group *grp_p;
|
|
Packit Service |
5956c7 |
int ret;
|
|
Packit Service |
5956c7 |
bool using_default_default_user = false;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!getpwnam_buf_len)
|
|
Packit Service |
5956c7 |
set_pwnam_buf_len();
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char buf[getpwnam_buf_len];
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (default_user && !username) {
|
|
Packit Service |
5956c7 |
using_default_default_user = true;
|
|
Packit Service |
5956c7 |
username = "keepalived_script";
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if ((ret = getpwnam_r(username, &pwd, buf, sizeof(buf), &pwd_p))) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to resolve %sscript username '%s' - ignoring", default_user ? "default " : "", username);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
if (!pwd_p) {
|
|
Packit Service |
5956c7 |
if (using_default_default_user)
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "WARNING - default user '%s' for script execution does not exist - please create.", username);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "%script user '%s' does not exist", default_user ? "Default s" : "S", username);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
uid = pwd.pw_uid;
|
|
Packit Service |
5956c7 |
gid = pwd.pw_gid;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (groupname) {
|
|
Packit Service |
5956c7 |
if ((ret = getgrnam_r(groupname, &grp, buf, sizeof(buf), &grp_p))) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to resolve %sscript group name '%s' - ignoring", default_user ? "default " : "", groupname);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
if (!grp_p) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "%script group '%s' does not exist", default_user ? "Default s" : "S", groupname);
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
gid = grp.gr_gid;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
*uid_p = uid;
|
|
Packit Service |
5956c7 |
*gid_p = gid;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* The default script user/group is keepalived_script if it exists, or root otherwise */
|
|
Packit Service |
5956c7 |
bool
|
|
Packit Service |
5956c7 |
set_default_script_user(const char *username, const char *groupname)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
if (!default_script_uid_set || username) {
|
|
Packit Service |
5956c7 |
/* Even if we fail to set it, there is no point in trying again */
|
|
Packit Service |
5956c7 |
default_script_uid_set = true;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (set_uid_gid(username, groupname, &default_script_uid, &default_script_gid, true)) {
|
|
Packit Service |
5956c7 |
if (username || script_security)
|
|
Packit Service |
5956c7 |
default_user_fail = true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
default_user_fail = false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return default_user_fail;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
bool
|
|
Packit Service |
5956c7 |
set_script_uid_gid(vector_t *strvec, unsigned keyword_offset, uid_t *uid_p, gid_t *gid_p)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
char *username;
|
|
Packit Service |
5956c7 |
char *groupname;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
username = strvec_slot(strvec, keyword_offset);
|
|
Packit Service |
5956c7 |
if (vector_size(strvec) > keyword_offset + 1)
|
|
Packit Service |
5956c7 |
groupname = strvec_slot(strvec, keyword_offset + 1);
|
|
Packit Service |
5956c7 |
else
|
|
Packit Service |
5956c7 |
groupname = NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return set_uid_gid(username, groupname, uid_p, gid_p, false);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
set_script_params_array(vector_t *strvec, notify_script_t *script, unsigned extra_params)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
unsigned num_words = 0;
|
|
Packit Service |
5956c7 |
size_t len = 0;
|
|
Packit Service |
5956c7 |
char **word_ptrs;
|
|
Packit Service |
5956c7 |
char *words;
|
|
Packit Service |
5956c7 |
vector_t *strvec_qe = NULL;
|
|
Packit Service |
5956c7 |
unsigned i;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Count the number of words, and total string length */
|
|
Packit Service |
5956c7 |
if (vector_size(strvec) >= 2)
|
|
Packit Service |
5956c7 |
strvec_qe = alloc_strvec_quoted_escaped(strvec_slot(strvec, 1));
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!strvec_qe)
|
|
Packit Service |
5956c7 |
return;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
num_words = vector_size(strvec_qe);
|
|
Packit Service |
5956c7 |
for (i = 0; i < num_words; i++)
|
|
Packit Service |
5956c7 |
len += strlen(strvec_slot(strvec_qe, i)) + 1;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Allocate memory for pointers to words and words themselves */
|
|
Packit Service |
5956c7 |
script->args = word_ptrs = MALLOC((num_words + extra_params + 1) * sizeof(char *) + len);
|
|
Packit Service |
5956c7 |
words = (char *)word_ptrs + (num_words + extra_params + 1) * sizeof(char *);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Set up pointers to words, and copy the words */
|
|
Packit Service |
5956c7 |
for (i = 0; i < num_words; i++) {
|
|
Packit Service |
5956c7 |
strcpy(words, strvec_slot(strvec_qe, i));
|
|
Packit Service |
5956c7 |
*(word_ptrs++) = words;
|
|
Packit Service |
5956c7 |
words += strlen(words) + 1;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
*word_ptrs = NULL;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
script->num_args = num_words;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
free_strvec(strvec_qe);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
notify_script_t*
|
|
Packit Service |
5956c7 |
notify_script_init(int extra_params, const char *type)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
notify_script_t *script = MALLOC(sizeof(notify_script_t));
|
|
Packit Service |
5956c7 |
vector_t *strvec_qe;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* We need to reparse the command line, allowing for quoted and escaped strings */
|
|
Packit Service |
5956c7 |
strvec_qe = alloc_strvec_quoted_escaped(NULL);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (!strvec_qe) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to parse notify script");
|
|
Packit Service |
5956c7 |
FREE(script);
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
set_script_params_array(strvec_qe, script, extra_params);
|
|
Packit Service |
5956c7 |
if (!script->args) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Unable to parse script '%s' - ignoring", FMT_STR_VSLOT(strvec_qe, 1));
|
|
Packit Service |
5956c7 |
FREE(script);
|
|
Packit Service |
5956c7 |
free_strvec(strvec_qe);
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
script->flags = 0;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (vector_size(strvec_qe) > 2) {
|
|
Packit Service |
5956c7 |
if (set_script_uid_gid(strvec_qe, 2, &script->uid, &script->gid)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Invalid user/group for %s script %s - ignoring", type, script->args[0]);
|
|
Packit Service |
5956c7 |
FREE(script->args);
|
|
Packit Service |
5956c7 |
FREE(script);
|
|
Packit Service |
5956c7 |
free_strvec(strvec_qe);
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
else {
|
|
Packit Service |
5956c7 |
if (set_default_script_user(NULL, NULL)) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "Failed to set default user for %s script %s - ignoring", type, script->args[0]);
|
|
Packit Service |
5956c7 |
FREE(script->args);
|
|
Packit Service |
5956c7 |
FREE(script);
|
|
Packit Service |
5956c7 |
free_strvec(strvec_qe);
|
|
Packit Service |
5956c7 |
return NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
script->uid = default_script_uid;
|
|
Packit Service |
5956c7 |
script->gid = default_script_gid;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
free_strvec(strvec_qe);
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return script;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
add_script_param(notify_script_t *script, char *param)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
/* We store the args as an array of pointers to the args, terminated
|
|
Packit Service |
5956c7 |
* by a NULL pointer, followed by the null terminated strings themselves
|
|
Packit Service |
5956c7 |
*/
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (script->args[script->num_args + 1]) {
|
|
Packit Service |
5956c7 |
log_message(LOG_INFO, "notify_fifo_script %s no room to add parameter %s", script->args[0], param);
|
|
Packit Service |
5956c7 |
return;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
/* Add the extra parameter in the pre-reserved slot at the end */
|
|
Packit Service |
5956c7 |
script->args[script->num_args++] = param;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
notify_resource_release(void)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
if (path_is_malloced) {
|
|
Packit Service |
5956c7 |
FREE(path);
|
|
Packit Service |
5956c7 |
path_is_malloced = false;
|
|
Packit Service |
5956c7 |
path = NULL;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
bool
|
|
Packit Service |
5956c7 |
notify_script_compare(notify_script_t *a, notify_script_t *b)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
int i;
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
if (a->num_args != b->num_args)
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
for (i = 0; i < a->num_args; i++) {
|
|
Packit Service |
5956c7 |
if (strcmp(a->args[i], b->args[i]))
|
|
Packit Service |
5956c7 |
return false;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
return true;
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
|
|
Packit Service |
5956c7 |
#ifdef THREAD_DUMP
|
|
Packit Service |
5956c7 |
void
|
|
Packit Service |
5956c7 |
register_notify_addresses(void)
|
|
Packit Service |
5956c7 |
{
|
|
Packit Service |
5956c7 |
register_thread_address("child_killed_thread", child_killed_thread);
|
|
Packit Service |
5956c7 |
}
|
|
Packit Service |
5956c7 |
#endif
|