]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Only schedule_exit() once
authorReynir Björnsson <reynir@reynir.dk>
Thu, 16 May 2024 11:58:08 +0000 (13:58 +0200)
committerGert Doering <gert@greenie.muc.de>
Thu, 16 May 2024 20:30:57 +0000 (22:30 +0200)
If an exit has already been scheduled we should not schedule it again.
Otherwise, the exit signal is never emitted if the peer reschedules the
exit before the timeout occurs.

schedule_exit() now only takes the context as argument. The signal is
hard coded to SIGTERM, and the interval is read directly from the
context options.

Furthermore, schedule_exit() now returns a bool signifying whether an
exit was scheduled; false if exit is already scheduled. The call sites
are updated accordingly. A notable difference is that management is only
notified *once* when an exit is scheduled - we no longer notify
management on redundant exit.

This patch was assigned a CVE number after already reviewed and ACKed,
because it was discovered that a misbehaving client can use the (now
fixed) server behaviour to avoid being disconnected by means of a
managment interface "client-kill" command - the security issue here is
"client can circumvent security policy set by management interface".

This only affects previously authenticated clients, and only management
client-kill, so normal renegotion / AUTH_FAIL ("your session ends") is not
affected.

CVE: 2024-28882

Change-Id: I9457f005f4ba970502e6b667d9dc4299a588d661
Signed-off-by: Reynir Björnsson <reynir@reynir.dk>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Message-Id: <20240516120434.23499-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg28679.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
src/openvpn/forward.c
src/openvpn/forward.h
src/openvpn/push.c

index 8d10f253403688355aa8d38a4d366a200c559b81..01165b2ed9262efffe7cfeaeea43e2a67202a51e 100644 (file)
@@ -514,17 +514,24 @@ check_server_poll_timeout(struct context *c)
 }
 
 /*
- * Schedule a signal n_seconds from now.
+ * Schedule a SIGTERM signal c->options.scheduled_exit_interval seconds from now.
  */
-void
-schedule_exit(struct context *c, const int n_seconds, const int signal)
+bool
+schedule_exit(struct context *c)
 {
+    const int n_seconds = c->options.scheduled_exit_interval;
+    /* don't reschedule if already scheduled. */
+    if (event_timeout_defined(&c->c2.scheduled_exit))
+    {
+        return false;
+    }
     tls_set_single_session(c->c2.tls_multi);
     update_time();
     reset_coarse_timers(c);
     event_timeout_init(&c->c2.scheduled_exit, n_seconds, now);
-    c->c2.scheduled_exit_signal = signal;
+    c->c2.scheduled_exit_signal = SIGTERM;
     msg(D_SCHED_EXIT, "Delayed exit in %d seconds", n_seconds);
+    return true;
 }
 
 /*
index 6fb5a1895b35b4e5226280cb8404737368401288..422c5913c8bf4d124666a187753b98061d334f99 100644 (file)
@@ -303,7 +303,7 @@ void reschedule_multi_process(struct context *c);
 
 void process_ip_header(struct context *c, unsigned int flags, struct buffer *buf);
 
-void schedule_exit(struct context *c, const int n_seconds, const int signal);
+bool schedule_exit(struct context *c);
 
 static inline struct link_socket_info *
 get_link_socket_info(struct context *c)
index 1b406b9c5311f420ddddbcc9c38e480e0b691650..d220eeb97442629467de1261cd654ab93dc1fbd3 100644 (file)
@@ -204,7 +204,11 @@ receive_exit_message(struct context *c)
      * */
     if (c->options.mode == MODE_SERVER)
     {
-        schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
+        if (!schedule_exit(c))
+        {
+            /* Return early when we don't need to notify management */
+            return;
+        }
     }
     else
     {
@@ -391,7 +395,7 @@ __attribute__ ((format(__printf__, 4, 5)))
 void
 send_auth_failed(struct context *c, const char *client_reason)
 {
-    if (event_timeout_defined(&c->c2.scheduled_exit))
+    if (!schedule_exit(c))
     {
         msg(D_TLS_DEBUG, "exit already scheduled for context");
         return;
@@ -401,8 +405,6 @@ send_auth_failed(struct context *c, const char *client_reason)
     static const char auth_failed[] = "AUTH_FAILED";
     size_t len;
 
-    schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
-
     len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
     if (len > PUSH_BUNDLE_SIZE)
     {
@@ -492,7 +494,7 @@ send_auth_pending_messages(struct tls_multi *tls_multi,
 void
 send_restart(struct context *c, const char *kill_msg)
 {
-    schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
+    schedule_exit(c);
     send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH);
 }