]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Implement exit notification via control channel
authorArne Schwabe <arne@rfc2549.org>
Wed, 14 Sep 2022 16:50:41 +0000 (18:50 +0200)
committerGert Doering <gert@greenie.muc.de>
Sun, 18 Sep 2022 14:30:32 +0000 (16:30 +0200)
Current exit notification relies on data channel messages with specific
prefix. Adding these to new data channel modules (DCO) adds unncessary
complexity for the data for messages that from their idea belong to the
control channel anyway.

This patch adds announcing support for control channel and sending/receving
it. We use the simple EXIT message for this.

Patch v2: add comment about protocol-flags to be not a user visible option,
          fix various grammar mistakes, remove unused argument to
          receive_exit_message

Patch v3: rename data_channel_crypto_flags to imported_protocol_flags
          add tls-ekm to protocol-flags.

Patch v4: rebase, use a buffer for the code that prepares the push reply

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Heiko Hund <heiko@ist.eigentlich.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20220914165041.2658423-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg25209.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
12 files changed:
doc/man-sections/client-options.rst
src/openvpn/crypto.h
src/openvpn/forward.c
src/openvpn/multi.c
src/openvpn/options.c
src/openvpn/options.h
src/openvpn/push.c
src/openvpn/push.h
src/openvpn/sig.c
src/openvpn/ssl.c
src/openvpn/ssl.h
src/openvpn/ssl_ncp.c

index 8e0e4f18a236fb88b36130978a8a2197d9b4cc20..5a906895bbf43346ccd12965c6fc04969d050ef5 100644 (file)
@@ -220,9 +220,14 @@ configuration.
   immediately close its client instance object rather than waiting for a
   timeout.
 
+  If both server and client support sending this message using the control
+  channel, the message will be sent as control-channel message. Otherwise
+  the message is sent as data-channel message, which will be ignored by
+  data-channel offloaded peers.
+
   The **n** parameter (default :code:`1` if not present) controls the
   maximum number of attempts that the client will try to resend the exit
-  notification message.
+  notification message if messages are sent in data-channel mode.
 
   In UDP server mode, send :code:`RESTART` control channel command to
   connected clients. The ``n`` parameter (default :code:`1` if not present)
index 98e2c766494df1e310a9afab0f7e5fb07356044f..5ea88908178c61e664a1da0c5f6fa68f9fc7e2dc 100644 (file)
@@ -264,6 +264,11 @@ struct crypto_options
     /**< Bit-flag indicating that we do not allow clients that do
      * not support resending the wrapped client key (WKc) with the
      * third packet of the three-way handshake */
+#define CO_USE_CC_EXIT_NOTIFY       (1<<6)
+    /**< Bit-flag indicating that explicit exit notifies should be
+     * sent via the control channel instead of using an OCC message
+     */
+
     unsigned int flags;         /**< Bit-flags determining behavior of
                                  *   security operation functions. */
 };
index 468eeee012415bc19330e885abceee92725a43ec..6a45b9e910b98b650de09dee223d0ed2b2a246ab 100644 (file)
@@ -260,6 +260,10 @@ check_incoming_control_channel(struct context *c)
         {
             receive_auth_pending(c, &buf);
         }
+        else if (buf_string_match_head_str(&buf, "EXIT"))
+        {
+            receive_exit_message(c);
+        }
         else
         {
             msg(D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR(&buf));
index 1bbeab7d478970e409555ed59d667739b61dd600..b9b087e01d84187d76fa124f61790dd789dfa2a7 100644 (file)
@@ -1801,10 +1801,15 @@ multi_client_set_protocol_options(struct context *c)
 #ifdef HAVE_EXPORT_KEYING_MATERIAL
     if (proto & IV_PROTO_TLS_KEY_EXPORT)
     {
-        o->data_channel_crypto_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
+        o->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
     }
 #endif
 
+    if (proto & IV_PROTO_CC_EXIT_NOTIFY)
+    {
+        o->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
+    }
+
     /* Select cipher if client supports Negotiable Crypto Parameters */
 
     /* if we have already created our key, we cannot *change* our own
index 45fbb634f8417d4554a87c18270896ee726863bf..e44993c03b93861ca76f0648b9305db2e11abed4 100644 (file)
@@ -3341,7 +3341,7 @@ pre_connect_restore(struct options *o, struct gc_arena *gc)
 
     o->push_continuation = 0;
     o->push_option_types_found = 0;
-    o->data_channel_crypto_flags = 0;
+    o->imported_protocol_flags = 0;
 }
 
 static void
@@ -8496,11 +8496,13 @@ add_option(struct options *options,
     }
     else if (streq(p[0], "key-derivation") && p[1])
     {
+        /* NCP only option that is pushed by the server to enable EKM,
+         * should not be used by normal users in config files*/
         VERIFY_PERMISSION(OPT_P_NCP)
 #ifdef HAVE_EXPORT_KEYING_MATERIAL
         if (streq(p[1], "tls-ekm"))
         {
-            options->data_channel_crypto_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
+            options->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
         }
         else
 #endif
@@ -8508,6 +8510,30 @@ add_option(struct options *options,
             msg(msglevel, "Unknown key-derivation method %s", p[1]);
         }
     }
+    else if (streq(p[0], "protocol-flags") && p[1])
+    {
+        /* NCP only option that is pushed by the server to enable protocol
+         * features that are negotiated, should not be used by normal users
+         * in config files */
+        VERIFY_PERMISSION(OPT_P_NCP)
+        for (size_t j = 1; j < MAX_PARMS && p[j] != NULL; j++)
+        {
+            if (streq(p[j], "cc-exit"))
+            {
+                options->imported_protocol_flags |= CO_USE_CC_EXIT_NOTIFY;
+            }
+#ifdef HAVE_EXPORT_KEYING_MATERIAL
+            else if (streq(p[j], "tls-ekm"))
+            {
+                options->imported_protocol_flags |= CO_USE_TLS_KEY_MATERIAL_EXPORT;
+            }
+#endif
+            else
+            {
+                msg(msglevel, "Unknown protocol-flags flag: %s", p[j]);
+            }
+        }
+    }
     else if (streq(p[0], "prng") && p[1] && !p[3])
     {
         msg(M_WARN, "NOTICE: --prng option ignored (SSL library PRNG is used)");
index b70cd42e0332724eb0d0a8baa4b2574e6b627afe..f438cd290060354528b555f2c13ece07e78fc357 100644 (file)
@@ -681,7 +681,7 @@ struct options
     bool allow_recursive_routing;
 
     /* data channel crypto flags set by push/pull. Reuses the CO_* crypto_flags */
-    unsigned int data_channel_crypto_flags;
+    unsigned int imported_protocol_flags;
 };
 
 #define streq(x, y) (!strcmp((x), (y)))
index 4cbc89e4c657fa392272614f38e775b1cdb97cb6..47a7992820dfba5ab48444d301f989988633d58c 100644 (file)
@@ -179,6 +179,21 @@ server_pushed_signal(struct context *c, const struct buffer *buffer, const bool
     }
 }
 
+void
+receive_exit_message(struct context *c)
+{
+    dmsg(D_STREAM_ERRORS, "Exit message received by peer");
+    c->sig->signal_received = SIGTERM;
+    c->sig->signal_text = "remote-exit";
+#ifdef ENABLE_MANAGEMENT
+    if (management)
+    {
+        management_notify(management, "info", c->sig->signal_text, "EXIT");
+    }
+#endif
+}
+
+
 void
 server_pushed_info(struct context *c, const struct buffer *buffer,
                    const int adv)
@@ -606,10 +621,30 @@ prepare_push_reply(struct context *c, struct gc_arena *gc,
     {
         push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
     }
-    if (o->data_channel_crypto_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT)
+
+    struct buffer proto_flags = alloc_buf_gc(128, gc);
+
+    if (o->imported_protocol_flags & CO_USE_CC_EXIT_NOTIFY)
+    {
+        buf_printf(&proto_flags, " cc-exit");
+
+        /* if the cc exit flag is supported, pushing tls-ekm via protocol-flags
+         * is also supported */
+        if (o->imported_protocol_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT)
+        {
+            buf_printf(&proto_flags, " tls-ekm");
+        }
+    }
+    else if (o->imported_protocol_flags & CO_USE_TLS_KEY_MATERIAL_EXPORT)
     {
         push_option_fmt(gc, push_list, M_USAGE, "key-derivation tls-ekm");
     }
+
+    if (buf_len(&proto_flags) > 0)
+    {
+        push_option_fmt(gc, push_list, M_USAGE, "protocol-flags%s", buf_str(&proto_flags));
+    }
+
     return true;
 }
 
index 62fad4a144c3059c0af4a64f4000d4fe3485aaa2..7138055a74e98e3f6c06c21808e106628f013311 100644 (file)
@@ -48,6 +48,8 @@ void receive_auth_failed(struct context *c, const struct buffer *buffer);
 
 void server_pushed_signal(struct context *c, const struct buffer *buffer, const bool restart, const int adv);
 
+void receive_exit_message(struct context *c);
+
 void server_pushed_info(struct context *c, const struct buffer *buffer,
                         const int adv);
 
index e06edd2161c8732b9b40aaa60e96bdf53fe25fa7..65cd25c65eb3a21a4147bc6ae4afcdf7ebc7532b 100644 (file)
@@ -321,20 +321,46 @@ print_status(const struct context *c, struct status_output *so)
     gc_free(&gc);
 }
 
+
+/* Small helper function to determine if we should send the exit notification
+ * via control channel */
+static inline bool
+cc_exit_notify_enabled(struct context *c)
+{
+    /* Check if we have TLS active at all */
+    if (!c->c2.tls_multi)
+    {
+        return false;
+    }
+
+    const struct key_state *ks = get_primary_key(c->c2.tls_multi);
+    return (ks->crypto_options.flags & CO_USE_CC_EXIT_NOTIFY);
+}
+
 /*
  * Handle the triggering and time-wait of explicit
  * exit notification.
  */
-
 static void
 process_explicit_exit_notification_init(struct context *c)
 {
     msg(M_INFO, "SIGTERM received, sending exit notification to peer");
+    /* init the timeout to send the OCC_EXIT messages if cc exit is not
+     * enabled and also to exit after waiting for retries of resending of
+     * exit messages */
     event_timeout_init(&c->c2.explicit_exit_notification_interval, 1, 0);
     reset_coarse_timers(c);
+
     signal_reset(c->sig);
     halt_non_edge_triggered_signals();
     c->c2.explicit_exit_notification_time_wait = now;
+
+    /* Check if we are in TLS mode and should send the notification via data
+     * channel */
+    if (cc_exit_notify_enabled(c))
+    {
+        send_control_channel_string(c, "EXIT", D_PUSH);
+    }
 }
 
 void
@@ -351,7 +377,7 @@ process_explicit_exit_notification_timer_wakeup(struct context *c)
             c->sig->signal_received = SIGTERM;
             c->sig->signal_text = "exit-with-notification";
         }
-        else
+        else if (!cc_exit_notify_enabled(c))
         {
             c->c2.occ_op = OCC_EXIT;
         }
index 33e145b3f8d33c2939e695b64b125b972d95feb1..d3f7a0203bd694010a19a46a8fccb4a05958fc35 100644 (file)
@@ -1719,7 +1719,7 @@ tls_session_update_crypto_params(struct tls_multi *multi,
     }
 
     /* Import crypto settings that might be set by pull/push */
-    session->opt->crypto_flags |= options->data_channel_crypto_flags;
+    session->opt->crypto_flags |= options->imported_protocol_flags;
 
     return tls_session_update_crypto_params_do_work(multi, session, options,
                                                     frame, frame_fragment, lsi);
@@ -1969,6 +1969,9 @@ push_peer_info(struct buffer *buf, struct tls_session *session)
         /* support for the --dns option */
         iv_proto |= IV_PROTO_DNS_OPTION;
 
+        /* support for exit notify via control channel */
+        iv_proto |= IV_PROTO_CC_EXIT_NOTIFY;
+
         /* support for receiving push_reply before sending
          * push request, also signal that the client wants
          * to get push-reply messages without without requiring a round
index 76b1b67436d307a87a9aaf8c259a510c392fc63d..12ffd44d76260c95a844c8b013eb1ecd7e6785e8 100644 (file)
 /** Supports the --dns option introduced in version 2.6 */
 #define IV_PROTO_DNS_OPTION      (1<<6)
 
+/** Support for explicit exit notify via control channel
+ *  This also includes support for the protocol-flags pushed option */
+#define IV_PROTO_CC_EXIT_NOTIFY  (1<<7)
+
 /* Default field in X509 to be username */
 #define X509_USERNAME_FIELD_DEFAULT "CN"
 
index a58ced537b23820a68083dff3db724eecc8b81e6..fe84919257228a3d9958eb73bb152f300ad17ac9 100644 (file)
@@ -419,6 +419,11 @@ p2p_ncp_set_options(struct tls_multi *multi, struct tls_session *session)
         multi->peer_id = 0x76706e; /* 'v' 'p' 'n' */
     }
 
+    if (iv_proto_peer & IV_PROTO_CC_EXIT_NOTIFY)
+    {
+        session->opt->crypto_flags |= CO_USE_CC_EXIT_NOTIFY;
+    }
+
 #if defined(HAVE_EXPORT_KEYING_MATERIAL)
     if (iv_proto_peer & IV_PROTO_TLS_KEY_EXPORT)
     {