From: Selva Nair
Currently we use the old signal API which follows system-V or
BSD semantics depending on the platform and/or feature-set macros.
Further, signal has many weaknesses which makes proper masking
(deferring) of signals during update not possible.
Improve this:
- Use sigaction to properly mask signals when modifying.
Notes:
Updating signal_reset() is handled in a follow up patch
SIG_SOURCE_CONNECTION_FAILED is retained in a hackish way. This value
has the same meaning as SIG_SOURCE_SOFT everywhere except where the
signal is printed. Looks cosmetic --- could be eliminated?
In pre_init_signal_catch() we ignore some unix signals, but the same signals
from management are not ignored though both are treated as "HARD" signals.
For example, during auth-user-pass query, "kill -SIGUSR1 " will be ignored,
but "signal SIGUSR1" from management interface will cause M_FATAL and exit.
This is the current behaviour, but could be improved?
This patch was originally submitted as 5/5 of the signals series. Now this is
1/2 of a new series with signal_reset changes moved to 2/2
Signed-off-by: Selva Nair
---
src/openvpn/errlevel.h | 1 +
src/openvpn/sig.c | 264 +++--
src/openvpn/socket.c | 1 -
3 files changed, 202 insertions(+), 64 deletions(-)
diff --git a/src/openvpn/errlevel.h b/src/openvpn/errlevel.h
index c69ea91d..dedc0790 100644
--- a/src/openvpn/errlevel.h
+++ b/src/openvpn/errlevel.h
@@ -115,6 +115,7 @@
#define D_CLIENT_NAT LOGLEV(6, 69, M_DEBUG) /* show client NAT debug
info */
#define D_XKEY LOGLEV(6, 69, M_DEBUG) /* show xkey-provider
debug info */
#define D_DCO_DEBUG LOGLEV(6, 69, M_DEBUG) /* show DCO related
lowlevel debug messages */
+#define D_SIGNAL_DEBUG LOGLEV(6, 69, M_DEBUG) /* show signal related
debug messages */
#define D_SHOW_KEYS LOGLEV(7, 70, M_DEBUG) /* show data channel
encryption keys */
#define D_SHOW_KEY_SOURCELOGLEV(7, 70, M_DEBUG) /* show data channel key
source entropy */
diff --git a/src/openvpn/sig.c b/src/openvpn/sig.c
index 0d534601..559ca35d 100644
--- a/src/openvpn/sig.c
+++ b/src/openvpn/sig.c
@@ -6,6 +6,7 @@
* packet compression.
*
* Copyright (C) 2002-2023 OpenVPN Inc
+ * Copyright (C) 2016-2023 Selva Nair
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -60,6 +61,9 @@ static const struct signame signames[] = {
{ SIGUSR2, 1, "SIGUSR2", "sigusr2" }
};
+/* mask for hard signals from management or windows */
+static unsigned long long ignored_hard_signals_mask;
+
int
parse_signal(const char *signame)
{
@@ -114,24 +118,144 @@ signal_description(const int signum, const char *sigtext)
}
}
+/**
+ * Block (i.e., defer) all unix signals.
+ * Used while directly modifying the volatile elements of
+ * siginfo_static.
+ */
+static inline void
+block_async_signals(void)
+{
+#ifndef _WIN32
+sigset_t all;
+sigfillset(); /* all signals */
+sigprocmask(SIG_BLOCK, , NULL);
+#endif
+}
+
+/**
+ * Unblock all unix signals.
+ */
+static inline void
+unblock_async_signals(void)
+{
+#ifndef _WIN32
+sigset_t none;
+sigemptyset();
+sigprocmask(SIG_SETMASK, , NULL);
+#endif
+}
+
+/**
+ * Private function for registering a signal in the specified
+ * signal_info struct. This could be the global siginfo_static
+ * or a context specific signinfo struct.
+ *
+ * A signal is allowed to override an already registered
+ * one only if it has a higher priority.
+ * Returns true if the signal is set, false otherwise.
+ *
+ * Do not call any "AS-unsafe" functions such as printf from here
+ * as this may be called from signal_handler().
+ */
+static bool
+try_throw_signal(struct signal_info *si, int signum, int source)
+{
+bool ret = false;
+if (signal_priority(signum) >= signal_priority(si->signal_received))
+{
+si->signal_received = signum;
+si->source = source;
+ret = true;
+}
+return ret;
+}
+
+/**
+ * Throw a hard signal. Called from management and when windows
+ * signals are received through ctrl-c, exit event etc.
+ */
void
throw_signal(const int signum)
{
-if (signal_priority(signum) >=
signal_priority(siginfo_static.signal_received))
+if (ignored_hard_signals_mask & (1LL << signum))
+{
+msg(D_SIGNAL_DEBUG, "Signal %s is currently ignored",
signal_name(signum, true));
+return;
+}
+block_async_signals();
+
+if (!try_throw_signal(_static, signum, SIG_SOURCE_HARD))
{
-siginfo_static.signal_received = signum;
-siginfo_static.source = SIG_SOURCE_HARD;
+msg(D_SIGNAL_DEBUG, "Ignoring %s when %s has been received",
signal_name(signum, true),
+signal_name(siginfo_static.signal_received, true));
}
+else
+{
+msg(D_SIGNAL_DEBUG, "Throw signal (hard): %s ",