]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Updated version to 2.1_rc7e.
authorjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Wed, 11 Jun 2008 08:45:09 +0000 (08:45 +0000)
committerjames <james@e7ae566f-a301-0410-adde-c780ea21d3b5>
Wed, 11 Jun 2008 08:45:09 +0000 (08:45 +0000)
Added client authentication and packet filtering capability
to management interface.

Extended packet filtering capability to work on both --dev tun
and --dev tap tunnels.

Updated valgrind-suppress file.

Made "Linux ip addr del failed" error nonfatal.

Amplified --client-cert-not-required warning.

Added #pragma pack to proto.h.

git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2991 e7ae566f-a301-0410-adde-c780ea21d3b5

30 files changed:
Makefile.am
buffer.c
buffer.h
debug/valgrind-suppress
dovalns [new file with mode: 0755]
errlevel.h
error.c
error.h
forward.c
init.c
manage.c
manage.h
management/management-notes.txt
mroute.c
mroute.h
multi.c
multi.h
openvpn.8
openvpn.h
options.c
options.h
pf-inline.h [new file with mode: 0644]
pf.c
pf.h
proto.h
ssl.c
ssl.h
syshead.h
tun.c
version.m4

index 6b94ccd5b6ccef73f8983ce6495e2f60e62e48c8..88a8379ba53b9a9a8f604ff50084b994e5ec01a2 100644 (file)
@@ -112,7 +112,7 @@ openvpn_SOURCES = \
        otime.c otime.h \
        packet_id.c packet_id.h \
        perf.c perf.h \
-       pf.c pf.h \
+       pf.c pf.h pf-inline.h \
        ping.c ping.h ping-inline.h \
        plugin.c plugin.h \
        pool.c pool.h \
index 6ceaacf703492a01e4131e4262ed28e0fce47144..92b10e56fbd353e704b23ca3cc95814bdb295043 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -819,3 +819,130 @@ valign4 (const struct buffer *buf, const char *file, const int line)
     }
 }
 #endif
+
+/*
+ * struct buffer_list
+ */
+
+#ifdef ENABLE_BUFFER_LIST
+
+struct buffer_list *
+buffer_list_new (const int max_size)
+{
+  struct buffer_list *ret;
+  ALLOC_OBJ_CLEAR (ret, struct buffer_list);
+  ret->max_size = max_size;
+  ret->size = 0;
+  return ret;
+}
+
+void
+buffer_list_free (struct buffer_list *ol)
+{
+  buffer_list_reset (ol);
+  free (ol);
+}
+
+bool
+buffer_list_defined (const struct buffer_list *ol)
+{
+  return ol->head != NULL;
+}
+
+void
+buffer_list_reset (struct buffer_list *ol)
+{
+  struct buffer_entry *e = ol->head;
+  while (e)
+    {
+      struct buffer_entry *next = e->next;
+      free_buf (&e->buf);
+      free (e);
+      e = next;
+    }
+  ol->head = ol->tail = NULL;
+  ol->size = 0;
+}
+
+void
+buffer_list_push (struct buffer_list *ol, const unsigned char *str)
+{
+  if (!ol->max_size || ol->size < ol->max_size)
+    {
+      struct buffer_entry *e;
+      ALLOC_OBJ_CLEAR (e, struct buffer_entry);
+
+      ++ol->size;
+      if (ol->tail)
+       {
+         ASSERT (ol->head);
+         ol->tail->next = e;
+       }
+      else
+       {
+         ASSERT (!ol->head);
+         ol->head = e;
+       }
+      e->buf = string_alloc_buf ((const char *) str, NULL);
+      ol->tail = e;
+    }
+}
+
+const struct buffer *
+buffer_list_peek (struct buffer_list *ol)
+{
+  if (ol->head)
+    return &ol->head->buf;
+  else
+    return NULL;
+}
+
+static void
+buffer_list_pop (struct buffer_list *ol)
+{
+  if (ol->head)
+    {
+      struct buffer_entry *e = ol->head->next;
+      free_buf (&ol->head->buf);
+      free (ol->head);
+      ol->head = e;
+      --ol->size;
+      if (!e)
+       ol->tail = NULL;
+    }
+}
+
+void
+buffer_list_advance (struct buffer_list *ol, int n)
+{
+  if (ol->head)
+    {
+      struct buffer *buf = &ol->head->buf;
+      ASSERT (buf_advance (buf, n));
+      if (!BLEN (buf))
+       buffer_list_pop (ol);
+    }
+}
+
+struct buffer_list *
+buffer_list_file (const char *fn, int max_line_len)
+{
+  FILE *fp = fopen (fn, "r");
+  struct buffer_list *bl = NULL;
+
+  if (fp)
+    {
+      char *line = (char *) malloc (max_line_len);
+      if (line)
+       {
+         bl = buffer_list_new (0);
+         while (fgets (line, max_line_len, fp) != NULL)
+           buffer_list_push (bl, (unsigned char *)line);
+         free (line);
+       }
+      fclose (fp);
+    }
+  return bl;
+}
+
+#endif
index eb37794fc6ef9a56a241442e1ed5e62feb02168a..195c7d36877164d9f20679aa2b2dea4f2532c15b 100644 (file)
--- a/buffer.h
+++ b/buffer.h
@@ -723,4 +723,38 @@ check_malloc_return (void *p)
     out_of_memory ();
 }
 
+/*
+ * Manage lists of buffers
+ */
+
+#ifdef ENABLE_BUFFER_LIST
+
+struct buffer_entry
+{
+  struct buffer buf;
+  struct buffer_entry *next;
+};
+
+struct buffer_list
+{
+  struct buffer_entry *head; /* next item to pop/peek */
+  struct buffer_entry *tail; /* last item pushed */
+  int size;                  /* current number of entries */
+  int max_size;              /* maximum size list should grow to */
+};
+
+struct buffer_list *buffer_list_new (const int max_size);
+void buffer_list_free (struct buffer_list *ol);
+
+bool buffer_list_defined (const struct buffer_list *ol);
+void buffer_list_reset (struct buffer_list *ol);
+
+void buffer_list_push (struct buffer_list *ol, const unsigned char *str);
+const struct buffer *buffer_list_peek (struct buffer_list *ol);
+void buffer_list_advance (struct buffer_list *ol, int n);
+
+struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
+
+#endif
+
 #endif /* BUFFER_H */
index 612c45ec6c7e5d1532444b0bcd5a571727b5073f..a94c61a788d52c5f020b273bebe572b5678e616c 100644 (file)
-# Valgrind suppressions file for OpenVPN.
-#
-# Mostly deal with uninitialized data warnings
-# in OpenSSL.
+{
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
+}
 
 {
-   cond_BN
-   Memcheck:Cond
-   fun:BN_*
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   fun:__nss_next
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   value4_BN
-   Memcheck:Value4
-   fun:BN_*
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   cond_bn
-   Memcheck:Cond
-   fun:bn_*
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlopen
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
 }
 
 {
-   value4_bn
-   Memcheck:Value4
-   fun:bn_*
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlopen
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
 }
 
 {
-   cond_SHA1_Update
-   Memcheck:Cond
-   fun:SHA1_Update
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlsym
+   fun:libdl_resolve_symbol
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
 }
 
 {
-   value4_SHA1_Update
-   Memcheck:Value4
-   fun:SHA1_Update
+   <insert a suppression name here>
+   Memcheck:Addr8
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlopen
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
 }
 
 {
-   cond_ssl3_read_bytes
+   <insert a suppression name here>
    Memcheck:Cond
-   fun:ssl3_read_bytes
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:*
+   obj:*
+   obj:*
 }
 
 {
-   cond_crypto
+   <insert a suppression name here>
    Memcheck:Cond
-   obj:/lib/libcrypto.so.*
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   fun:__nss_next
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   value4_crypto
-   Memcheck:Value4
-   obj:/lib/libcrypto.so.*
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
 }
 
 {
-   cond_ssl
+   <insert a suppression name here>
    Memcheck:Cond
-   obj:/lib/libssl.so.*
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   value4_ssl
-   Memcheck:Value4
-   obj:/lib/libssl.so.*
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   fun:__nss_next
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   addr4_AES_cbc_encrypt
-   Memcheck:Addr4
-   fun:AES_cbc_encrypt
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
 }
 
 {
-   cond_memcpy_ssl3_read_bytes
+   <insert a suppression name here>
    Memcheck:Cond
-   fun:memcpy
-   fun:ssl3_read_bytes
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   value4_memcpy_ssl3_read_bytes
-   Memcheck:Value4
-   fun:memcpy
-   fun:ssl3_read_bytes
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   fun:__nss_next
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
-   cond_memset_BUF_MEM_grow_clean
+   <insert a suppression name here>
    Memcheck:Cond
-   fun:memset
-   fun:BUF_MEM_grow_clean
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
 }
 
 {
-   value4_memset_BUF_MEM_grow_clean
-   Memcheck:Value4
-   fun:memset
-   fun:BUF_MEM_grow_clean
+   <insert a suppression name here>
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_udp
+   fun:main
 }
 
 {
    <insert a suppression name here>
-   Memcheck:Addr8
+   Memcheck:Cond
+   obj:/lib/ld-2.5.so
    obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libc-2.5.so
+   obj:/lib/ld-2.5.so
+   fun:__libc_dlopen_mode
+   fun:__nss_lookup_function
+   obj:/lib/libc-2.5.so
+   fun:gethostbyname_r
+   fun:gethostbyname
+   fun:getaddr
+   fun:resolve_remote
+   fun:link_socket_init_phase1
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
 }
 
 {
    <insert a suppression name here>
    Memcheck:Cond
    obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   obj:/lib/ld-2.5.so
+   obj:/lib/libdl-2.5.so
+   fun:dlopen
+   fun:plugin_list_init
+   fun:init_plugins
+   fun:main
 }
 
 {
    fun:init_static
    fun:main
 }
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_udp
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:getdelim
+   fun:getpass
+   fun:get_console_input
+   fun:get_user_pass
+   fun:context_init_1
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:tsearch
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:tsearch
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   fun:tsearch
+   fun:__nss_lookup_function
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_udp
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   obj:/lib/libc-2.5.so
+   fun:__nss_database_lookup
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   obj:/lib/libc-2.5.so
+   fun:__nss_database_lookup
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_tcp
+   fun:main
+}
+
+{
+   <insert a suppression name here>
+   Memcheck:Leak
+   fun:malloc
+   obj:/lib/libc-2.5.so
+   fun:__nss_database_lookup
+   obj:*
+   obj:*
+   fun:getgrnam_r
+   fun:getgrnam
+   fun:get_group
+   fun:do_init_first_time
+   fun:init_instance
+   fun:init_instance_handle_signals
+   fun:tunnel_server_udp
+   fun:main
+}
+
diff --git a/dovalns b/dovalns
new file mode 100755 (executable)
index 0000000..482ae3f
--- /dev/null
+++ b/dovalns
@@ -0,0 +1,2 @@
+#!/bin/bash
+valgrind --tool=memcheck --error-limit=no --gen-suppressions=all --leak-check=full --show-reachable=yes --num-callers=32 $*
index 652bf39e3678fa63a64d8151a768d6f710066745..b2a0ddae834f316a3c99302ae77e11f9d393250c 100644 (file)
@@ -94,7 +94,7 @@
 #define D_ROUTE_QUOTA        LOGLEV(3, 43, 0)        /* show route quota exceeded messages */
 #define D_OSBUF              LOGLEV(3, 44, 0)        /* show socket/tun/tap buffer sizes */
 #define D_PS_PROXY           LOGLEV(3, 45, 0)        /* messages related to --port-share option */
-#define D_PF                 LOGLEV(3, 46, 0)        /* messages related to packet filter */
+#define D_PF_INFO            LOGLEV(3, 46, 0)        /* packet filter informational messages */
 
 #define D_SHOW_PARMS         LOGLEV(4, 50, 0)        /* show all parameters on program initiation */
 #define D_SHOW_OCC           LOGLEV(4, 51, 0)        /* show options compatibility string */
 #define D_DHCP_OPT           LOGLEV(4, 53, 0)        /* show DHCP options binary string */
 #define D_MBUF               LOGLEV(4, 54, 0)        /* mbuf.[ch] routines */
 #define D_PACKET_TRUNC_ERR   LOGLEV(4, 55, 0)        /* PACKET_TRUNCATION_CHECK */
+#define D_PF_DROPPED         LOGLEV(4, 56, 0)        /* packet filter dropped a packet */
 
 #define D_LOG_RW             LOGLEV(5, 0,  0)        /* Print 'R' or 'W' to stdout for read/write */
 
 #define D_PING               LOGLEV(7, 70, M_DEBUG)  /* PING send/receive messages */
 #define D_PS_PROXY_DEBUG     LOGLEV(7, 70, M_DEBUG)  /* port share proxy debug */
 #define D_AUTO_USERID        LOGLEV(7, 70, M_DEBUG)  /* AUTO_USERID debugging */
+#define D_PF_DROPPED_BCAST   LOGLEV(7, 71, M_DEBUG)  /* packet filter dropped a broadcast packet */
+#define D_PF_DEBUG           LOGLEV(7, 72, M_DEBUG)  /* packet filter debugging, must also define PF_DEBUG in pf.h */
 
 #define D_HANDSHAKE_VERBOSE  LOGLEV(8, 70, M_DEBUG)  /* show detailed description of each handshake */
 #define D_TLS_DEBUG_MED      LOGLEV(8, 70, M_DEBUG)  /* limited info from tls_session routines */
diff --git a/error.c b/error.c
index bb5909a7ec33247cb93a6d5070add9458cda624c..cdc2a11130c67a267df7107e40ee647b2dfc1405 100644 (file)
--- a/error.c
+++ b/error.c
@@ -268,7 +268,10 @@ void x_msg (const unsigned int flags, const char *format, ...)
 #endif
 
   /* set up client prefix */
-  prefix = msg_get_prefix ();
+  if (flags & M_NOIPREFIX)
+    prefix = NULL;
+  else
+    prefix = msg_get_prefix ();
   prefix_sep = " ";
   if (!prefix)
     prefix_sep = prefix = "";
diff --git a/error.h b/error.h
index f2eaa1263588064509995c5208636ec8680483a3..0f3087b998487f4cab6a08be60cd506bc9667f47 100644 (file)
--- a/error.h
+++ b/error.h
@@ -102,13 +102,14 @@ extern int x_msg_line_num;
 #define M_MSG_VIRT_OUT    (1<<14)        /* output message through msg_status_output callback */
 #define M_OPTERR          (1<<15)        /* print "Options error:" prefix */
 #define M_NOLF            (1<<16)        /* don't print new line */
+#define M_NOIPREFIX       (1<<17)        /* don't print instance prefix */
 
 /* flag combinations which are frequently used */
 #define M_ERR     (M_FATAL | M_ERRNO)
 #define M_SOCKERR (M_FATAL | M_ERRNO_SOCK)
 #define M_SSLERR  (M_FATAL | M_SSL)
 #define M_USAGE   (M_USAGE_SMALL | M_NOPREFIX | M_OPTERR)
-#define M_CLIENT  (M_MSG_VIRT_OUT|M_NOMUTE)
+#define M_CLIENT  (M_MSG_VIRT_OUT | M_NOMUTE | M_NOIPREFIX)
 
 /*
  * Mute levels are designed to avoid large numbers of
@@ -126,6 +127,11 @@ extern int x_msg_line_num;
  * log_level:  verbosity level n (--verb n) must be >= log_level to print.
  * mute_level: don't print more than n (--mute n) consecutive messages at
  *             a given mute level, or if 0 disable muting and print everything.
+ *
+ * Mask map:
+ * Bits 0-3:   log level
+ * Bits 4-23:  M_x flags
+ * Bits 24-31: mute level
  */
 #define LOGLEV(log_level, mute_level, other) ((log_level) | ENCODE_MUTE_LEVEL(mute_level) | other)
 
index b7c8b3b3728e128185d384795ba04ab653209129..85c11944728d8c0aa91462449072f43b3328a0c0 100644 (file)
--- a/forward.c
+++ b/forward.c
@@ -492,7 +492,7 @@ process_coarse_timers (struct context *c)
   check_push_request (c);
 #endif
 
-#ifdef ENABLE_PF
+#ifdef PLUGIN_PF
   pf_check_reload (c);
 #endif
 
diff --git a/init.c b/init.c
index 9d80d1a1a1c20b63208c5801d2ea4df0ca9f3a7a..97ecf7d37518fe236776a98276ac0305b86142b2 100644 (file)
--- a/init.c
+++ b/init.c
@@ -1558,6 +1558,10 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
 
   to.plugins = c->plugins;
 
+#ifdef MANAGEMENT_DEF_AUTH
+  to.mda_context = &c->c2.mda_context;
+#endif
+
 #if P2MP_SERVER
   to.auth_user_pass_verify_script = options->auth_user_pass_verify_script;
   to.auth_user_pass_verify_script_via_file = options->auth_user_pass_verify_script_via_file;
@@ -2356,7 +2360,7 @@ open_plugins (struct context *c, const bool import_options, int init_point)
                {
                  unsigned int option_types_found = 0;
                  if (config.list[i] && config.list[i]->value)
-                   options_plugin_import (&c->options,
+                   options_string_import (&c->options,
                                           config.list[i]->value,
                                           D_IMPORT_ERRORS|M_OPTERR,
                                           OPT_P_DEFAULT & ~OPT_P_PLUGIN,
@@ -2452,21 +2456,19 @@ open_management (struct context *c)
     {
       if (c->options.management_addr)
        {
+         unsigned int flags = c->options.management_flags;
+         if (c->options.mode == MODE_SERVER)
+           flags |= MF_SERVER;
          if (management_open (management,
                               c->options.management_addr,
                               c->options.management_port,
                               c->options.management_user_pass,
-                              c->options.mode == MODE_SERVER,
-                              c->options.management_query_passwords,
                               c->options.management_log_history_cache,
                               c->options.management_echo_buffer_size,
                               c->options.management_state_buffer_size,
-                              c->options.management_hold,
-                              c->options.management_signal,
-                              c->options.management_forget_disconnect,
-                              c->options.management_client,
                               c->options.management_write_peer_info_file,
-                              c->options.remap_sigusr1))
+                              c->options.remap_sigusr1,
+                              flags))
            {
              management_set_state (management,
                                    OPENVPN_STATE_CONNECTING,
@@ -2792,6 +2794,11 @@ close_instance (struct context *c)
        /* close TUN/TAP device */
        do_close_tun (c, false);
 
+#ifdef MANAGEMENT_DEF_AUTH
+       if (management)
+         management_notify_client_close (management, &c->c2.mda_context, NULL);
+#endif
+
 #ifdef ENABLE_PF
        pf_destroy_context (&c->c2.pf);
 #endif
index 090f691cccc06fbc63cb348695279f1406ddc452..24cd6b50a685c5fb4fe4bba1321f3b6fda541952 100644 (file)
--- a/manage.c
+++ b/manage.c
@@ -87,6 +87,15 @@ man_help ()
 #ifdef ENABLE_PKCS11
   msg (M_CLIENT, "pkcs11-id-count        : Get number of available PKCS#11 identities.");
   msg (M_CLIENT, "pkcs11-id-get index    : Get PKCS#11 identity at index.");
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+  msg (M_CLIENT, "client-auth CID KID    : Authenticate client-id/key-id CID/KID (MULTILINE)");
+  msg (M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID");
+  msg (M_CLIENT, "client-deny CID KID R  : Deny auth client-id/key-id CID/KID with reason text R");
+  msg (M_CLIENT, "client-kill CID        : Kill client instance CID");
+#ifdef MANAGEMENT_PF
+  msg (M_CLIENT, "client-pf CID          : Define packet filter for client CID (MULTILINE)");
+#endif
 #endif
   msg (M_CLIENT, "signal s               : Send signal s to daemon,");
   msg (M_CLIENT, "                         s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
@@ -178,7 +187,7 @@ man_update_io_state (struct management *man)
 {
   if (socket_defined (man->connection.sd_cli))
     {
-      if (output_list_defined (man->connection.out))
+      if (buffer_list_defined (man->connection.out))
        {
          man->connection.state = MS_CC_WAIT_WRITE;
        }
@@ -195,7 +204,7 @@ man_output_list_push (struct management *man, const char *str)
   if (management_connected (man))
     {
       if (str)
-       output_list_push (man->connection.out, (const unsigned char *) str);
+       buffer_list_push (man->connection.out, (const unsigned char *) str);
       man_update_io_state (man);
       if (!man->persist.standalone_disabled)
        {
@@ -672,12 +681,12 @@ man_hold (struct management *man, const char *cmd)
     {
       if (streq (cmd, "on"))
        {
-         man->settings.hold = true;
+         man->settings.flags |= MF_HOLD;
          msg (M_CLIENT, "SUCCESS: hold flag set to ON");
        }
       else if (streq (cmd, "off"))
        {
-         man->settings.hold = false;
+         man->settings.flags &= ~MF_HOLD;
          msg (M_CLIENT, "SUCCESS: hold flag set to OFF");
        }
       else if (streq (cmd, "release"))
@@ -691,9 +700,205 @@ man_hold (struct management *man, const char *cmd)
        }
     }
   else
-    msg (M_CLIENT, "SUCCESS: hold=%d", (int) man->settings.hold);
+    msg (M_CLIENT, "SUCCESS: hold=%d", BOOL_CAST(man->settings.flags & MF_HOLD));
+}
+
+#ifdef MANAGEMENT_DEF_AUTH
+
+static bool
+parse_cid (const char *str, unsigned long *cid)
+{
+  if (sscanf (str, "%lu", cid) == 1)
+    return true;
+  else
+    {
+      msg (M_CLIENT, "ERROR: cannot parse CID");
+      return false;
+    }
+}
+
+static bool
+parse_kid (const char *str, unsigned int *kid)
+{
+  if (sscanf (str, "%u", kid) == 1)
+    return true;
+  else
+    {
+      msg (M_CLIENT, "ERROR: cannot parse KID");
+      return false;
+    }
+}
+
+static void
+in_extra_reset (struct man_connection *mc, const bool new)
+{
+  if (mc)
+    {
+      if (!new)
+       {
+         mc->in_extra_cmd = IEC_UNDEF;
+         mc->in_extra_cid = 0;
+         mc->in_extra_kid = 0;
+       }
+      if (mc->in_extra)
+       {
+         buffer_list_free (mc->in_extra);
+         mc->in_extra = NULL;
+       }
+      if (new)
+       mc->in_extra = buffer_list_new (0);
+    }
+}
+
+static void
+in_extra_dispatch (struct management *man)
+{
+   switch (man->connection.in_extra_cmd)
+    {
+    case IEC_CLIENT_AUTH:
+       if (man->persist.callback.client_auth)
+       {
+         const bool status = (*man->persist.callback.client_auth)
+           (man->persist.callback.arg,
+            man->connection.in_extra_cid,
+            man->connection.in_extra_kid,
+            true,
+            NULL,
+            man->connection.in_extra);
+         man->connection.in_extra = NULL;
+         if (status)
+           {
+             msg (M_CLIENT, "SUCCESS: client-auth command succeeded");
+           }
+         else
+           {
+             msg (M_CLIENT, "ERROR: client-auth command failed");
+           }
+       }
+      else
+       {
+         msg (M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode");
+       }
+      break;
+#ifdef MANAGEMENT_PF
+    case IEC_CLIENT_PF:
+      if (man->persist.callback.client_pf)
+       {
+         const bool status = (*man->persist.callback.client_pf)
+           (man->persist.callback.arg,
+            man->connection.in_extra_cid,
+            man->connection.in_extra);
+         man->connection.in_extra = NULL;
+         if (status)
+           {
+             msg (M_CLIENT, "SUCCESS: client-pf command succeeded");
+           }
+         else
+           {
+             msg (M_CLIENT, "ERROR: client-pf command failed");
+           }
+       }
+      else
+       {
+         msg (M_CLIENT, "ERROR: The client-pf command is not supported by the current daemon mode");
+       }
+      break;
+#endif
+    }
+   in_extra_reset (&man->connection, false);
 }
 
+static void
+man_client_auth (struct management *man, const char *cid_str, const char *kid_str, const bool extra)
+{
+  struct man_connection *mc = &man->connection;
+  mc->in_extra_cid = 0;
+  mc->in_extra_kid = 0;
+  if (parse_cid (cid_str, &mc->in_extra_cid)
+      && parse_kid (kid_str, &mc->in_extra_kid))
+    {
+      mc->in_extra_cmd = IEC_CLIENT_AUTH;
+      in_extra_reset (mc, true);
+      if (!extra)
+       in_extra_dispatch (man);
+    }
+}
+
+static void
+man_client_deny (struct management *man, const char *cid_str, const char *kid_str, const char *reason)
+{
+  unsigned long cid = 0;
+  unsigned int kid = 0;
+  if (parse_cid (cid_str, &cid) && parse_kid (kid_str, &kid))
+    {
+      if (man->persist.callback.client_auth)
+       {
+         const bool status = (*man->persist.callback.client_auth)
+           (man->persist.callback.arg,
+            cid,
+            kid,
+            false,
+            reason,
+            NULL);
+         if (status)
+           {
+             msg (M_CLIENT, "SUCCESS: client-deny command succeeded");
+           }
+         else
+           {
+             msg (M_CLIENT, "ERROR: client-deny command failed");
+           }
+       }
+      else
+       {
+         msg (M_CLIENT, "ERROR: The client-deny command is not supported by the current daemon mode");
+       }
+    }
+}
+
+static void
+man_client_kill (struct management *man, const char *cid_str)
+{
+  unsigned long cid = 0;
+  if (parse_cid (cid_str, &cid))
+    {
+      if (man->persist.callback.kill_by_cid)
+       {
+         const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid);
+         if (status)
+           {
+             msg (M_CLIENT, "SUCCESS: client-kill command succeeded");
+           }
+         else
+           {
+             msg (M_CLIENT, "ERROR: client-kill command failed");
+           }
+       }
+      else
+       {
+         msg (M_CLIENT, "ERROR: The client-kill command is not supported by the current daemon mode");
+       }
+    }
+}
+
+#ifdef MANAGEMENT_PF
+
+static void
+man_client_pf (struct management *man, const char *cid_str)
+{
+  struct man_connection *mc = &man->connection;
+  mc->in_extra_cid = 0;
+  mc->in_extra_kid = 0;
+  if (parse_cid (cid_str, &mc->in_extra_cid))
+    {
+      mc->in_extra_cmd = IEC_CLIENT_PF;
+      in_extra_reset (mc, true);
+    }
+}
+
+#endif
+#endif
+
 #define MN_AT_LEAST (1<<0)
 
 static bool
@@ -867,6 +1072,35 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
       if (man_need (man, p, 1, 0))
        man_bytecount (man, atoi(p[1]));
     }
+#ifdef MANAGEMENT_DEF_AUTH
+  else if (streq (p[0], "client-kill"))
+    {
+      if (man_need (man, p, 1, 0))
+       man_client_kill (man, p[1]);
+    }
+  else if (streq (p[0], "client-deny"))
+    {
+      if (man_need (man, p, 3, 0))
+       man_client_deny (man, p[1], p[2], p[3]);
+    }
+  else if (streq (p[0], "client-auth-nt"))
+    {
+      if (man_need (man, p, 2, 0))
+       man_client_auth (man, p[1], p[2], false);
+    }
+  else if (streq (p[0], "client-auth"))
+    {
+      if (man_need (man, p, 2, 0))
+       man_client_auth (man, p[1], p[2], true);
+    }
+#ifdef MANAGEMENT_PF
+  else if (streq (p[0], "client-pf"))
+    {
+      if (man_need (man, p, 1, 0))
+       man_client_pf (man, p[1]);
+    }
+#endif
+#endif
 #ifdef ENABLE_PKCS11
   else if (streq (p[0], "pkcs11-id-count"))
     {
@@ -999,7 +1233,7 @@ man_new_connection_post (struct management *man, const char *description)
        description,
        print_sockaddr (&man->settings.local, &gc));
 
-  output_list_reset (man->connection.out);
+  buffer_list_reset (man->connection.out);
 
   if (!man_password_needed (man))
     man_welcome (man);
@@ -1134,14 +1368,17 @@ man_reset_client_socket (struct management *man, const bool exiting)
       man_close_socket (man, man->connection.sd_cli);
       man->connection.sd_cli = SOCKET_UNDEFINED;
       command_line_reset (man->connection.in);
-      output_list_reset (man->connection.out);
+      buffer_list_reset (man->connection.out);
+#ifdef MANAGEMENT_DEF_AUTH
+      in_extra_reset (&man->connection, false);
+#endif
     }
   if (!exiting)
     {
-      if (man->settings.management_forget_disconnect)
+      if (man->settings.flags & MF_FORGET_DISCONNECT)
         ssl_purge_auth ();
 
-      if (man->settings.signal_on_disconnect) {
+      if (man->settings.flags & MF_SIGNAL) {
          int mysig = man_mod_signal (man, SIGUSR1);
          if (mysig >= 0)
            {
@@ -1150,7 +1387,7 @@ man_reset_client_socket (struct management *man, const bool exiting)
            }
       }
 
-      if (man->settings.connect_as_client)
+      if (man->settings.flags & MF_CONNECT_AS_CLIENT)
        {
          msg (D_MANAGEMENT, "MANAGEMENT: Triggering management exit");
          throw_signal_soft (SIGTERM, "management-exit");
@@ -1170,6 +1407,9 @@ man_process_command (struct management *man, const char *line)
 
   CLEAR (parms);
   so = status_open (NULL, 0, -1, &man->persist.vout, 0);
+#ifdef MANAGEMENT_DEF_AUTH
+  in_extra_reset (&man->connection, false);
+#endif
 
   if (man_password_needed (man))
     {
@@ -1243,7 +1483,7 @@ man_read (struct management *man)
       /*
        * Reset output object
        */
-      output_list_reset (man->connection.out);
+      buffer_list_reset (man->connection.out);
 
       /*
        * process command line if complete
@@ -1252,7 +1492,22 @@ man_read (struct management *man)
        const unsigned char *line;
        while ((line = command_line_get (man->connection.in)))
          {
-           man_process_command (man, (char *) line);
+#ifdef MANAGEMENT_DEF_AUTH
+           if (man->connection.in_extra)
+             {
+               if (!strcmp ((char *)line, "END"))
+                 {
+                   in_extra_dispatch (man);
+                   in_extra_reset (&man->connection, false);
+                 }
+               else
+                 {
+                   buffer_list_push (man->connection.in_extra, line);
+                 }
+             }
+           else
+#endif
+             man_process_command (man, (char *) line);
            if (man->connection.halt)
              break;
            command_line_next (man->connection.in);
@@ -1289,14 +1544,14 @@ man_write (struct management *man)
   const int max_send = 256;
   int sent = 0;
 
-  const struct buffer *buf = output_list_peek (man->connection.out);
+  const struct buffer *buf = buffer_list_peek (man->connection.out);
   if (buf && BLEN (buf))
     {
       const int len = min_int (max_send, BLEN (buf));
       sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL);
       if (sent >= 0)
        {
-         output_list_advance (man->connection.out, sent);
+         buffer_list_advance (man->connection.out, sent);
        }
       else if (sent < 0)
        {
@@ -1387,27 +1642,18 @@ man_settings_init (struct man_settings *ms,
                   const char *addr,
                   const int port,
                   const char *pass_file,
-                  const bool server,
-                  const bool query_passwords,
                   const int log_history_cache,
                   const int echo_buffer_size,
                   const int state_buffer_size,
-                  const bool hold,
-                  const bool signal_on_disconnect,
-                  const bool management_forget_disconnect,
-                  const bool connect_as_client,
                   const char *write_peer_info_file,
-                  const int remap_sigusr1)
+                  const int remap_sigusr1,
+                  const unsigned int flags)
 {
   if (!ms->defined)
     {
       CLEAR (*ms);
 
-      /*
-       * Are we a server?  If so, it will influence
-       * the way we handle state transitions.
-       */
-      ms->server = server;
+      ms->flags = flags;
 
       /*
        * Get username/password
@@ -1415,34 +1661,6 @@ man_settings_init (struct man_settings *ms,
       if (pass_file)
        get_user_pass (&ms->up, pass_file, "Management", GET_USER_PASS_PASSWORD_ONLY);
 
-      /*
-       * Should OpenVPN query the management layer for
-       * passwords?
-       */
-      ms->up_query_passwords = query_passwords;
-
-      /*
-       * Should OpenVPN hibernate on startup?
-       */
-      ms->hold = hold;
-
-      /*
-       * Should OpenVPN be signaled if management
-       * disconnects?
-       */
-      ms->signal_on_disconnect = signal_on_disconnect;
-
-      /*
-       * Should OpenVPN forget passwords when managmenet
-       * session disconnects?
-       */
-      ms->management_forget_disconnect = management_forget_disconnect;
-
-      /*
-       * Should OpenVPN connect to management interface as a client
-       * rather than a server?
-       */
-      ms->connect_as_client = connect_as_client;
       ms->write_peer_info_file = string_alloc (write_peer_info_file, NULL);
 
       /*
@@ -1456,7 +1674,7 @@ man_settings_init (struct man_settings *ms,
        * Run management over tunnel, or
        * separate channel?
        */
-      if (streq (addr, "tunnel") && !connect_as_client)
+      if (streq (addr, "tunnel") && !(flags & MF_CONNECT_AS_CLIENT))
        {
          ms->management_over_tunnel = true;
        }
@@ -1511,7 +1729,7 @@ man_connection_init (struct management *man)
        * command output from/to the socket.
        */
       man->connection.in = command_line_new (256);
-      man->connection.out = output_list_new (0);
+      man->connection.out = buffer_list_new (0);
 
       /*
        * Initialize event set for standalone usage, when we are
@@ -1525,7 +1743,7 @@ man_connection_init (struct management *man)
       /*
        * Listen/connect socket
        */
-      if (man->settings.connect_as_client)
+      if (man->settings.flags & MF_CONNECT_AS_CLIENT)
        man_connect (man);
       else
        man_listen (man);
@@ -1549,7 +1767,10 @@ man_connection_close (struct management *man)
   if (mc->in)
     command_line_free (mc->in);
   if (mc->out)
-    output_list_free (mc->out);
+    buffer_list_free (mc->out);
+#ifdef MANAGEMENT_DEF_AUTH
+  in_extra_reset (&man->connection, false);
+#endif
   man_connection_clear (mc);
 }
 
@@ -1574,17 +1795,12 @@ management_open (struct management *man,
                 const char *addr,
                 const int port,
                 const char *pass_file,
-                const bool server,
-                const bool query_passwords,
                 const int log_history_cache,
                 const int echo_buffer_size,
                 const int state_buffer_size,
-                const bool hold,
-                const bool signal_on_disconnect,
-                const bool management_forget_disconnect,
-                const bool connect_as_client,
                 const char *write_peer_info_file,
-                const int remap_sigusr1)
+                const int remap_sigusr1,
+                const unsigned int flags)
 {
   bool ret = false;
 
@@ -1596,17 +1812,12 @@ management_open (struct management *man,
                     addr,
                     port,
                     pass_file,
-                    server,
-                    query_passwords,
                     log_history_cache,
                     echo_buffer_size,
                     state_buffer_size,
-                    hold,
-                    signal_on_disconnect,
-                    management_forget_disconnect,
-                    connect_as_client,
                     write_peer_info_file,
-                    remap_sigusr1);
+                    remap_sigusr1,
+                    flags);
 
   /*
    * The log is initially sized to MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
@@ -1665,7 +1876,7 @@ management_set_state (struct management *man,
                      const in_addr_t tun_local_ip,
                      const in_addr_t tun_remote_ip)
 {
-  if (man->persist.state && (!man->settings.server || state < OPENVPN_STATE_CLIENT_BASE))
+  if (man->persist.state && (!(man->settings.flags & MF_SERVER) || state < OPENVPN_STATE_CLIENT_BASE))
     {
       struct gc_arena gc = gc_new ();
       struct log_entry e;
@@ -1697,6 +1908,79 @@ management_set_state (struct management *man,
     }
 }
 
+#ifdef MANAGEMENT_DEF_AUTH
+
+static void
+man_output_env (const struct env_set *es)
+{
+  if (es)
+    {
+      struct env_item *e;
+      for (e = es->list; e != NULL; e = e->next)
+       {
+         if (e->string)
+           msg (M_CLIENT, ">CLIENT:ENV,%s", e->string);
+       }
+    }
+  msg (M_CLIENT, ">CLIENT:ENV,END");
+}
+
+void
+management_notify_client_needing_auth (struct management *management,
+                                      const unsigned int mda_key_id,
+                                      struct man_def_auth_context *mdac,
+                                      const struct env_set *es)
+{
+  if (!(mdac->flags & DAF_CONNECTION_CLOSED))
+    {
+      const char *mode = "CONNECT";
+      if (mdac->flags & DAF_CONNECTION_ESTABLISHED)
+       mode = "REAUTH";
+      msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
+      man_output_env (es);
+      mdac->flags |= DAF_INITIAL_AUTH;
+    }
+}
+
+void
+management_connection_established (struct management *management,
+                                  struct man_def_auth_context *mdac)
+{
+  mdac->flags |= DAF_CONNECTION_ESTABLISHED;
+}
+
+void
+management_notify_client_close (struct management *management,
+                               struct man_def_auth_context *mdac,
+                               const struct env_set *es)
+{
+  if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
+    {
+      msg (M_CLIENT, ">CLIENT:DISCONNECT,%lu", mdac->cid);
+      man_output_env (es);
+      mdac->flags |= DAF_CONNECTION_CLOSED;
+    }
+}
+
+void
+management_learn_addr (struct management *management,
+                      struct man_def_auth_context *mdac,
+                      const struct mroute_addr *addr,
+                      const bool primary)
+{
+  struct gc_arena gc = gc_new ();
+  if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
+    {
+      msg (M_CLIENT, ">CLIENT:ADDRESS,%lu,%s,%d",
+          mdac->cid,
+          mroute_addr_print_ex (addr, MAPF_SUBNET, &gc),
+          BOOL_CAST (primary));
+    }
+  gc_free (&gc);
+}
+
+#endif
+
 void
 management_echo (struct management *man, const char *string, const bool pull)
 {
@@ -2178,7 +2462,7 @@ management_query_user_pass (struct management *man,
 bool
 management_would_hold (struct management *man)
 {
-  return man->settings.hold && !man->persist.hold_release && man_standalone_ok (man);
+  return (man->settings.flags & MF_HOLD) && !man->persist.hold_release && man_standalone_ok (man);
 }
 
 /*
@@ -2188,7 +2472,7 @@ management_would_hold (struct management *man)
 bool
 management_should_daemonize (struct management *man)
 {
-  return management_would_hold (man) || man->settings.up_query_passwords;
+  return management_would_hold (man) || (man->settings.flags & MF_QUERY_PASSWORDS);
 }
 
 /*
@@ -2301,108 +2585,6 @@ command_line_next (struct command_line *cl)
   buf_clear (&cl->residual);
 }
 
-/*
- * struct output_list
- */
-
-struct output_list *
-output_list_new (const int max_size)
-{
-  struct output_list *ret;
-  ALLOC_OBJ_CLEAR (ret, struct output_list);
-  ret->max_size = max_size;
-  ret->size = 0;
-  return ret;
-}
-
-void
-output_list_free (struct output_list *ol)
-{
-  output_list_reset (ol);
-  free (ol);
-}
-
-bool
-output_list_defined (const struct output_list *ol)
-{
-  return ol->head != NULL;
-}
-
-void
-output_list_reset (struct output_list *ol)
-{
-  struct output_entry *e = ol->head;
-  while (e)
-    {
-      struct output_entry *next = e->next;
-      free_buf (&e->buf);
-      free (e);
-      e = next;
-    }
-  ol->head = ol->tail = NULL;
-  ol->size = 0;
-}
-
-void
-output_list_push (struct output_list *ol, const unsigned char *str)
-{
-  if (!ol->max_size || ol->size < ol->max_size)
-    {
-      struct output_entry *e;
-      ALLOC_OBJ_CLEAR (e, struct output_entry);
-
-      ++ol->size;
-      if (ol->tail)
-       {
-         ASSERT (ol->head);
-         ol->tail->next = e;
-       }
-      else
-       {
-         ASSERT (!ol->head);
-         ol->head = e;
-       }
-      e->buf = string_alloc_buf ((const char *) str, NULL);
-      ol->tail = e;
-    }
-}
-
-const struct buffer *
-output_list_peek (struct output_list *ol)
-{
-  if (ol->head)
-    return &ol->head->buf;
-  else
-    return NULL;
-}
-
-static void
-output_list_pop (struct output_list *ol)
-{
-  if (ol->head)
-    {
-      struct output_entry *e = ol->head->next;
-      free_buf (&ol->head->buf);
-      free (ol->head);
-      ol->head = e;
-      --ol->size;
-      if (!e)
-       ol->tail = NULL;
-    }
-}
-
-void
-output_list_advance (struct output_list *ol, int n)
-{
-  if (ol->head)
-    {
-      struct buffer *buf = &ol->head->buf;
-      ASSERT (buf_advance (buf, n));
-      if (!BLEN (buf))
-       output_list_pop (ol);
-    }
-}
-
 /*
  * struct log_entry
  */
index fed14b79642bc83e3ac4b10f7495e9ed1ea26ef4..b4d75bfa9c51705145875c2b581b638055eb90e2 100644 (file)
--- a/manage.h
+++ b/manage.h
@@ -30,6 +30,7 @@
 #include "misc.h"
 #include "event.h"
 #include "socket.h"
+#include "mroute.h"
 
 #define MANAGEMENT_VERSION                      1
 #define MANAGEMENT_N_PASSWORD_RETRIES           3
 #define MANAGEMENT_ECHO_BUFFER_SIZE           100
 #define MANAGEMENT_STATE_BUFFER_SIZE          100
 
+/*
+ * Management-interface-based deferred authentication
+ */
+#ifdef MANAGEMENT_DEF_AUTH
+struct man_def_auth_context {
+  unsigned long cid;
+
+#define DAF_CONNECTION_ESTABLISHED (1<<0)
+#define DAF_CONNECTION_CLOSED      (1<<1)
+#define DAF_INITIAL_AUTH           (1<<2)
+  unsigned int flags;
+
+  unsigned int mda_key_id_counter;
+};
+#endif
+
 /*
  * Manage build-up of command line
  */
@@ -54,34 +71,6 @@ const unsigned char *command_line_get (struct command_line *cl);
 void command_line_reset (struct command_line *cl);
 void command_line_next (struct command_line *cl);
 
-/*
- * Manage lists of output strings
- */
-
-struct output_entry
-{
-  struct buffer buf;
-  struct output_entry *next;
-};
-
-struct output_list
-{
-  struct output_entry *head; /* next item to pop/peek */
-  struct output_entry *tail; /* last item pushed */
-  int size;                  /* current number of entries */
-  int max_size;              /* maximum size list should grow to */
-};
-
-struct output_list *output_list_new (const int max_size);
-void output_list_free (struct output_list *ol);
-
-bool output_list_defined (const struct output_list *ol);
-void output_list_reset (struct output_list *ol);
-
-void output_list_push (struct output_list *ol, const unsigned char *str);
-const struct buffer *output_list_peek (struct output_list *ol);
-void output_list_advance (struct output_list *ol, int n);
-
 /*
  * Manage log file history
  */
@@ -148,7 +137,8 @@ log_history_capacity (const struct log_history *h)
 }
 
 /*
- * Callbacks for 'status' and 'kill' commands
+ * Callbacks for 'status' and 'kill' commands.
+ * Also for management-based deferred authentication and packet filter.
  */
 struct management_callback
 {
@@ -158,6 +148,20 @@ struct management_callback
   int (*kill_by_cn) (void *arg, const char *common_name);
   int (*kill_by_addr) (void *arg, const in_addr_t addr, const int port);
   void (*delete_event) (void *arg, event_t event);
+#ifdef MANAGEMENT_DEF_AUTH
+  bool (*kill_by_cid) (void *arg, const unsigned long cid);
+  bool (*client_auth) (void *arg,
+                      const unsigned long cid,
+                      const unsigned int mda_key_id,
+                      const bool auth,
+                      const char *reason,
+                      struct buffer_list *cc_config); /* ownership transferred */
+#endif
+#ifdef MANAGEMENT_PF
+  bool (*client_pf) (void *arg,
+                    const unsigned long cid,
+                    struct buffer_list *pf_config);   /* ownership transferred */
+#endif
 };
 
 /*
@@ -196,18 +200,13 @@ struct man_persist {
 
 struct man_settings {
   bool defined;
+  unsigned int flags; /* MF_x flags */
   struct openvpn_sockaddr local;
-  bool up_query_passwords;
   bool management_over_tunnel;
   struct user_pass up;
   int log_history_cache;
   int echo_buffer_size;
   int state_buffer_size;
-  bool server;
-  bool hold;
-  bool signal_on_disconnect;
-  bool management_forget_disconnect;
-  bool connect_as_client;
   char *write_peer_info_file;
 
 /* flags for handling the management interface "signal" command */
@@ -246,8 +245,17 @@ struct man_connection {
   int password_tries;
 
   struct command_line *in;
-  struct output_list *out;
-
+  struct buffer_list *out;
+
+#ifdef MANAGEMENT_DEF_AUTH
+# define IEC_UNDEF       0
+# define IEC_CLIENT_AUTH 1
+# define IEC_CLIENT_PF   2
+  int in_extra_cmd;
+  unsigned long in_extra_cid;
+  unsigned int in_extra_kid;
+  struct buffer_list *in_extra;
+#endif
   struct event_set *es;
 
   bool state_realtime;
@@ -274,21 +282,29 @@ struct user_pass;
 
 struct management *management_init (void);
 
+/* management_open flags */
+# define MF_SERVER            (1<<0)
+# define MF_QUERY_PASSWORDS   (1<<1)
+# define MF_HOLD              (1<<2)
+# define MF_SIGNAL            (1<<3)
+# define MF_FORGET_DISCONNECT (1<<4)
+# define MF_CONNECT_AS_CLIENT (1<<5)
+#ifdef MANAGEMENT_DEF_AUTH
+# define MF_CLIENT_AUTH       (1<<6)
+#endif
+#ifdef MANAGEMENT_PF
+# define MF_CLIENT_PF         (1<<7)
+#endif
 bool management_open (struct management *man,
                      const char *addr,
                      const int port,
                      const char *pass_file,
-                     const bool server,
-                     const bool query_passwords,
                      const int log_history_cache,
                      const int echo_buffer_size,
                      const int state_buffer_size,
-                     const bool hold,
-                     const bool signal_on_disconnect,
-                     const bool management_forget_disconnect,
-                     const bool connect_as_client,
                      const char *write_peer_info_file,
-                     const int remap_sigusr1);
+                     const int remap_sigusr1,
+                     const unsigned int flags);
 
 void management_close (struct management *man);
 
@@ -316,6 +332,25 @@ bool management_hold (struct management *man);
 
 void management_event_loop_n_seconds (struct management *man, int sec);
 
+#ifdef MANAGEMENT_DEF_AUTH
+void management_notify_client_needing_auth (struct management *management,
+                                           const unsigned int auth_id,
+                                           struct man_def_auth_context *mdac,
+                                           const struct env_set *es);
+
+void management_connection_established (struct management *management,
+                                       struct man_def_auth_context *mdac);
+
+void management_notify_client_close (struct management *management,
+                                    struct man_def_auth_context *mdac,
+                                    const struct env_set *es);
+
+void management_learn_addr (struct management *management,
+                           struct man_def_auth_context *mdac,
+                           const struct mroute_addr *addr,
+                           const bool primary);
+#endif
+
 static inline bool
 management_connected (const struct management *man)
 {
@@ -325,9 +360,25 @@ management_connected (const struct management *man)
 static inline bool
 management_query_user_pass_enabled (const struct management *man)
 {
-  return man->settings.up_query_passwords;
+  return BOOL_CAST(man->settings.flags & MF_QUERY_PASSWORDS);
 }
 
+#ifdef MANAGEMENT_PF
+static inline bool
+management_enable_pf (const struct management *man)
+{
+  return man && BOOL_CAST(man->settings.flags & MF_CLIENT_PF);
+}
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+static inline bool
+management_enable_def_auth (const struct management *man)
+{
+  return man && BOOL_CAST(man->settings.flags & MF_CLIENT_AUTH);
+}
+#endif
+
 /*
  * OpenVPN tells the management layer what state it's in
  */
index 73f82a51b2b297f8d23ce2711adb9023b19b6807..4d8611145f9c4e3c7ac2b8a6b69bd3d1aa2dfd6c 100644 (file)
@@ -25,13 +25,12 @@ Future versions of the management interface may allow out-of-band
 connections (i.e. not over the VPN) and secured with SSL/TLS.
 
 The management interface is enabled in the OpenVPN
-configuration file using the following directives:
+configuration file using the following directive:
 
 --management
---management-query-passwords
---management-log-cache
 
-See the man page for documentation on these directives.
+See the man page for documentation on this and related
+directives.
 
 Once OpenVPN has started with the management layer enabled,
 you can telnet to the management port (make sure to use
@@ -444,6 +443,199 @@ Example:
      pkcs11-id-get 1
      PKCS11ID-ENTRY:'1', ID:'<snip>', BLOB:'<snip>'
 
+COMMAND -- client-auth  (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request and specify
+"client-connect" configuration directives in a subsequent text block.
+
+The OpenVPN server should have been started with the
+--management-client-auth directive so that it will ask the management
+interface to approve client connections.
+
+
+  client-auth {CID} {KID}
+  line_1
+  line_2
+  ...
+  line_n
+  END
+
+CID,KID -- client ID and Key ID.  See documentation for ">CLIENT:"
+notification for more info.
+
+line_1 to line_n -- client-connect configuration text block, as would be
+returned by a --client-connect script.  The text block may be null, with
+"END" immediately following the "client-auth" line (using a null text
+block is equivalent to using the client-auth-nt command).
+
+A client-connect configuration text block contains OpenVPN directives
+that will be applied to the client instance object representing a newly
+connected client.
+
+COMMAND -- client-auth-nt  (OpenVPN 2.1 or higher)
+--------------------------------------------------
+
+Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request without specifying
+client-connect configuration text.
+
+The OpenVPN server should have been started with the
+--management-client-auth directive so that it will ask the management
+interface to approve client connections.
+
+  client-auth-nt {CID} {KID}
+
+CID,KID -- client ID and Key ID.  See documentation for ">CLIENT:"
+notification for more info.
+
+COMMAND -- client-deny  (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Deny a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request.
+
+  client-deny {CID} {KID} "reason-text"
+
+CID,KID -- client ID and Key ID.  See documentation for ">CLIENT:"
+notification for more info.
+
+reason-text: a human-readable message explaining why the authentication
+request was denied.  This message will be output to the OpenVPN log
+file or syslog.
+
+Note that client-deny denies a specific Key ID (pertaining to a
+TLS renegotiation).  A client-deny command issued in response to
+an initial TLS key negotiation (notified by ">CLIENT:CONNECT") will
+terminate the client session after returning "AUTH-FAILED" to the client.
+On the other hand, a client-deny command issued in response to
+a TLS renegotiation (">CLIENT:REAUTH") will invalidate the renegotiated
+key, however the TLS session associated with the currently active
+key will continue to live for up to --tran-window seconds before
+expiration.
+
+To immediately kill a client session, use "client-kill".
+
+COMMAND -- client-kill  (OpenVPN 2.1 or higher)
+-----------------------------------------------
+
+Immediately kill a client instance by CID.
+
+  client-kill {CID}
+
+CID -- client ID.  See documentation for ">CLIENT:" notification for more
+info.
+
+COMMAND -- client-pf  (OpenVPN 2.1 or higher)
+---------------------------------------------
+
+Push a packet filter file to a specific client.
+
+The OpenVPN server should have been started with the
+--management-client-pf directive so that it will require that
+VPN tunnel packets sent or received by client instances must
+conform to that client's packet filter configuration.
+
+  client-pf {CID}
+  line_1
+  line_2
+  ...
+  line_n
+  END
+
+CID -- client ID.  See documentation for ">CLIENT:" notification for
+more info.
+
+line_1 to line_n -- the packet filter configuration file for this
+client.
+
+Packet filter file grammar:
+
+ [CLIENTS DROP|ACCEPT]
+ {+|-}common_name1
+ {+|-}common_name2
+ . . .
+ [SUBNETS DROP|ACCEPT]
+ {+|-}subnet1
+ {+|-}subnet2
+ . . .
+ [END]
+
+ Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS | "unknown"
+
+ CLIENTS refers to the set of clients (by their common-name) which
+ this instance is allowed ('+') to connect to, or is excluded ('-')
+ from connecting to.  Note that in the case of client-to-client
+ connections, such communication must be allowed by the packet filter
+ configuration files of both clients AND the --client-to-client
+ directive must have been specified in the OpenVPN server config.
+
+ SUBNETS refers to IP addresses or IP address subnets which this
+ client instance may connect to ('+') or is excluded ('-') from
+ connecting to, and applies to IPv4 and ARP packets.  The special
+ "unknown" tag refers to packets of unknown type, i.e. a packet that
+ is not IPv4 or ARP.
+
+ DROP or ACCEPT defines default policy when there is no explicit match
+ for a common-name or subnet.  The [END] tag must exist.
+
+ Notes:
+
+ * The SUBNETS section currently only supports IPv4 addresses and
+   subnets.
+
+ * A given client or subnet rule applies to both incoming and
+   outgoing packets.
+
+ * The CLIENTS list is order-invariant.  Because the list is stored
+   as a hash-table, the order of the list does not affect its function.
+
+ * The SUBNETS table is scanned sequentially, and the first item to
+   match is chosen.  Therefore the SUBNETS table is NOT order-invariant.
+
+ * No client-to-client communication is allowed unless the
+   --client-to-client configuration directive is enabled AND
+   the CLIENTS list of BOTH clients allows the communication.
+
+Example packet filter spec, as transmitted to the management interface:
+
+ client-pf 42
+ [CLIENTS ACCEPT]
+ -accounting
+ -enigma
+ [SUBNETS DROP]
+ -10.46.79.9
+ +10.0.0.0/8
+ [END]
+ END
+
+The above example sets the packet filter policy for the client
+identified by CID=42.  This client may connect to all other clients
+except those having a common name of "accounting" or "enigma".
+The client may only interact with external IP addresses in the
+10.0.0.0/8 subnet, however access to 10.46.79.9 is specifically
+excluded.
+
+Another example packet filter spec, as transmitted to the
+management interface:
+
+ client-pf 99
+ [CLIENTS DENY]
+ +public
+ [SUBNETS ACCEPT]
+ +10.10.0.1
+ -10.0.0.0/8
+ -unknown
+ [END]
+ END
+
+The above example sets the packet filter policy for the client
+identified by CID=99.  This client may not connect to any other
+clients except those having a common name of "public".  It may
+interact with any external IP address except those in the
+10.0.0.0/8 netblock.  However interaction with one address in
+the 10.0.0.0/8 netblock is allowed: 10.10.0.1.  Also, the client
+may not interact with external IP addresses using an "unknown"
+protocol (i.e. one that is not IPv4 or ARP).
+
 OUTPUT FORMAT
 -------------
 
@@ -454,7 +646,7 @@ OUTPUT FORMAT
     the last line will be "END".
 
 (3) Real-time messages will be in the form ">[source]:[text]",
-    where source is "ECHO", "FATAL", "HOLD", "INFO", "LOG",
+    where source is "CLIENT", "ECHO", "FATAL", "HOLD", "INFO", "LOG",
     "NEED-OK", "PASSWORD", or "STATE".
 
 REAL-TIME MESSAGE FORMAT
@@ -469,6 +661,12 @@ column and are immediately followed by a type keyword
 indicating the type of real-time message.  The following
 types are currently defined:
 
+CLIENT   -- Notification of client connections and disconnections
+            on an OpenVPN server.  Enabled when OpenVPN is started
+            with the --management-client-auth option.  CLIENT
+            notifications may be multi-line.  See "The CLIENT
+            notification" section below for detailed info.
+
 ECHO     -- Echo messages as controlled by the "echo" command.
 
 FATAL    -- A fatal error which is output to the log file just
@@ -497,6 +695,60 @@ PASSWORD -- Used to tell the management client that OpenVPN
 STATE    -- Shows the current OpenVPN state, as controlled
             by the "state" command.
 
+The CLIENT notification
+-----------------------
+
+The ">CLIENT:" notification is enabled by the --management-client-auth
+OpenVPN configuration directive that gives the management interface client
+the responsibility to authenticate OpenVPN clients after their client
+certificate has been verified.  CLIENT notifications may be multi-line, and
+the sequentiality of a given CLIENT notification, its associated environmental
+variables, and the terminating ">CLIENT:ENV,END" line are guaranteed to be
+atomic.
+
+CLIENT notification types:
+
+(1) Notify new client connection ("CONNECT") or existing client TLS session
+    renegotiation ("REAUTH").  Information about the client is provided
+    by a list of environmental variables which are documented in the OpenVPN
+    man page.  The environmental variables passed are equivalent to those
+    that would be passed to an --auth-user-pass-verify script.
+
+    >CLIENT:CONNECT|REAUTH,{CID},{KID}
+    >CLIENT:ENV,name1=val1
+    >CLIENT:ENV,name2=val2
+    >CLIENT:ENV,...
+    >CLIENT:ENV,END
+
+(2) Notify existing client disconnection.  The environmental variables passed
+    are equivalent to those that would be passed to a --client-disconnect
+    script.
+
+    >CLIENT:DISCONNECT,{CID}
+    >CLIENT:ENV,name1=val1
+    >CLIENT:ENV,name2=val2
+    >CLIENT:ENV,...
+    >CLIENT:ENV,END
+
+(3) Notify that a particular virtual address or subnet
+    is now associated with a specific client.
+
+    >CLIENT:ADDRESS,{CID},{ADDR},{PRI}
+
+Variables:
+
+CID --  Client ID, numerical ID for each connecting client, sequence = 0,1,2,...
+KID --  Key ID, numerical ID for the key associated with a given client TLS session,
+        sequence = 0,1,2,...
+PRI --  Primary (1) or Secondary (0) VPN address/subnet.  All clients have at least
+        one primary IP address.  Secondary address/subnets are associated with
+        client-specific "iroute" directives.
+ADDR -- IPv4 address/subnet in the form 1.2.3.4 or 1.2.3.0/255.255.255.0
+
+In the unlikely scenario of an extremely long-running OpenVPN server,
+CID and KID should be assumed to recycle to 0 after (2^32)-1, however this
+recycling behavior is guaranteed to be collision-free.
+
 Command Parsing
 ---------------
 
index 5922b8a783290dffb0f780659dc9dee9fab6f127..5b383a9cdfbbe0c8fc057da8e948dcda6b2fdd80 100644 (file)
--- a/mroute.c
+++ b/mroute.c
@@ -76,87 +76,144 @@ mroute_learnable_address (const struct mroute_addr *addr)
   return not_all_zeros && not_all_ones && !is_mac_mcast_maddr (addr);
 }
 
-/*
- * Given a raw packet in buf, return the src and dest
- * addresses of the packet.
- */
+static inline void
+mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int mask)
+{
+  if (ma)
+    {
+      ma->type = MR_ADDR_IPV4 | mask;
+      ma->netbits = 0;
+      ma->len = 4;
+      *(in_addr_t*)ma->addr = src;
+    }
+}
+
+static inline bool
+mroute_is_mcast (const in_addr_t addr)
+{
+  return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
+}
+
+#ifdef ENABLE_PF
+
+static unsigned int
+mroute_extract_addr_arp (struct mroute_addr *src,
+                        struct mroute_addr *dest,
+                        const struct buffer *buf)
+{
+  unsigned int ret = 0;
+  if (BLEN (buf) >= (int) sizeof (struct openvpn_arp))
+    {
+      const struct openvpn_arp *arp = (const struct openvpn_arp *) BPTR (buf);
+      if (arp->mac_addr_type == htons(0x0001)
+         && arp->proto_addr_type == htons(0x0800)
+         && arp->mac_addr_size == 0x06
+         && arp->proto_addr_size == 0x04)
+       {
+         mroute_get_in_addr_t (src, arp->ip_src, MR_ARP);
+         mroute_get_in_addr_t (dest, arp->ip_dest, MR_ARP);
+
+         /* multicast packet? */
+         if (mroute_is_mcast (arp->ip_dest))
+           ret |= MROUTE_EXTRACT_MCAST;
+
+         ret |= MROUTE_EXTRACT_SUCCEEDED;
+       }
+    }
+  return ret;
+}
+
+#endif
+
 unsigned int
-mroute_extract_addr_from_packet (struct mroute_addr *src,
-                                struct mroute_addr *dest,
-                                struct buffer *buf,
-                                int tunnel_type)
+mroute_extract_addr_ipv4 (struct mroute_addr *src,
+                         struct mroute_addr *dest,
+                         const struct buffer *buf)
 {
   unsigned int ret = 0;
-  verify_align_4 (buf);
-  if (tunnel_type == DEV_TYPE_TUN)
+  if (BLEN (buf) >= 1)
     {
-      if (BLEN (buf) >= 1)
+      switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
        {
-         switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
+       case 4:
+         if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
            {
-           case 4:
-             if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
-               {
-                 const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR (buf);
-                 if (src)
-                   {
-                     src->type = MR_ADDR_IPV4;
-                     src->netbits = 0;
-                     src->len = 4;
-                     memcpy (src->addr, &ip->saddr, 4);
-                   }
-                 if (dest)
-                   {
-                     dest->type = MR_ADDR_IPV4;
-                     dest->netbits = 0;
-                     dest->len = 4;
-                     memcpy (dest->addr, &ip->daddr, 4);
-
-                     /* mcast address? */
-                     if ((ip->daddr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK))
-                       ret |= MROUTE_EXTRACT_MCAST;
-
-                     /* IGMP message? */
-                     if (ip->protocol == OPENVPN_IPPROTO_IGMP)
-                       ret |= MROUTE_EXTRACT_IGMP;
-                   }
-                 ret |= MROUTE_EXTRACT_SUCCEEDED;
-               }
-             break;
-           case 6:
-             {
-               msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet"); 
-               break;
-             }
+             const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR (buf);
+
+             mroute_get_in_addr_t (src, ip->saddr, 0);
+             mroute_get_in_addr_t (dest, ip->daddr, 0);
+
+             /* multicast packet? */
+             if (mroute_is_mcast (ip->daddr))
+               ret |= MROUTE_EXTRACT_MCAST;
+
+             /* IGMP message? */
+             if (ip->protocol == OPENVPN_IPPROTO_IGMP)
+               ret |= MROUTE_EXTRACT_IGMP;
+
+             ret |= MROUTE_EXTRACT_SUCCEEDED;
            }
+         break;
+       case 6:
+         {
+           msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet"); 
+           break;
+         }
        }
     }
-  else if (tunnel_type == DEV_TYPE_TAP)
+  return ret;
+}
+
+unsigned int
+mroute_extract_addr_ether (struct mroute_addr *src,
+                          struct mroute_addr *dest,
+                          struct mroute_addr *esrc,
+                          struct mroute_addr *edest,
+                          const struct buffer *buf)
+{
+  unsigned int ret = 0;
+  if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
     {
-      if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
+      const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf);
+      if (src)
        {
-         const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf);
-         if (src)
-           {
-             src->type = MR_ADDR_ETHER;
-             src->netbits = 0;
-             src->len = 6;
-             memcpy (src->addr, eth->source, 6);
-           }
-         if (dest)
+         src->type = MR_ADDR_ETHER;
+         src->netbits = 0;
+         src->len = 6;
+         memcpy (src->addr, eth->source, 6);
+       }
+      if (dest)
+       {
+         dest->type = MR_ADDR_ETHER;
+         dest->netbits = 0;
+         dest->len = 6;
+         memcpy (dest->addr, eth->dest, 6);
+
+         /* ethernet broadcast/multicast packet? */
+         if (is_mac_mcast_addr (eth->dest))
+           ret |= MROUTE_EXTRACT_BCAST;
+       }
+         
+      ret |= MROUTE_EXTRACT_SUCCEEDED;
+
+#ifdef ENABLE_PF
+      if (esrc || edest)
+       {
+         struct buffer b = *buf;
+         if (buf_advance (&b, sizeof (struct openvpn_ethhdr)))
            {
-             dest->type = MR_ADDR_ETHER;
-             dest->netbits = 0;
-             dest->len = 6;
-             memcpy (dest->addr, eth->dest, 6);
-
-             /* ethernet broadcast/multicast packet? */
-             if (is_mac_mcast_addr (eth->dest))
-               ret |= MROUTE_EXTRACT_BCAST;
+             switch (ntohs (eth->proto))
+               {
+               case OPENVPN_ETH_P_IPV4:
+                 ret |= (mroute_extract_addr_ipv4 (esrc, edest, &b) << MROUTE_SEC_SHIFT);
+                 break;
+               case OPENVPN_ETH_P_ARP:
+                 ret |= (mroute_extract_addr_arp (esrc, edest, &b) << MROUTE_SEC_SHIFT);
+                 break;
+               }
            }
-         
-         ret |= MROUTE_EXTRACT_SUCCEEDED;
        }
+#endif
     }
   return ret;
 }
@@ -228,6 +285,14 @@ mroute_addr_compare_function (const void *key1, const void *key2)
 const char *
 mroute_addr_print (const struct mroute_addr *ma,
                   struct gc_arena *gc)
+{
+  return mroute_addr_print_ex (ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
+}
+
+const char *
+mroute_addr_print_ex (const struct mroute_addr *ma,
+                     const unsigned int flags,
+                     struct gc_arena *gc)
 {
   struct buffer out = alloc_buf_gc (64, gc);
   if (ma)
@@ -249,9 +314,19 @@ mroute_addr_print (const struct mroute_addr *ma,
            addr = buf_read_u32 (&buf, &status);
            if (status)
              {
-               buf_printf (&out, "%s", print_in_addr_t (addr, IA_EMPTY_IF_UNDEF, gc));
+               if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
+                 buf_printf (&out, "ARP/");
+               buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
                if (maddr.type & MR_WITH_NETBITS)
-                 buf_printf (&out, "/%d", maddr.netbits);
+                 {
+                   if (flags & MAPF_SUBNET)
+                     {
+                       const in_addr_t netmask = netbits_to_netmask (maddr.netbits);
+                       buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc));
+                     }
+                   else
+                     buf_printf (&out, "/%d", maddr.netbits);
+                 }
              }
            if (maddr.type & MR_WITH_PORT)
              {
index 16d2add1c7d238ae20281ce88f3a3fc44e2d6db8..4a6f458ddf3d7851034383b44b6afee7169c06be 100644 (file)
--- a/mroute.h
+++ b/mroute.h
 #define IP_MCAST_NETWORK      ((in_addr_t)224<<24)
 
 /* Return status values for mroute_extract_addr_from_packet */
-#define MROUTE_EXTRACT_SUCCEEDED (1<<1)
-#define MROUTE_EXTRACT_BCAST     (1<<2)
-#define MROUTE_EXTRACT_MCAST     (1<<3)
-#define MROUTE_EXTRACT_IGMP      (1<<4)
+
+#define MROUTE_EXTRACT_SUCCEEDED (1<<0)
+#define MROUTE_EXTRACT_BCAST     (1<<1)
+#define MROUTE_EXTRACT_MCAST     (1<<2)
+#define MROUTE_EXTRACT_IGMP      (1<<3)
+
+#define MROUTE_SEC_EXTRACT_SUCCEEDED (1<<(0+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_BCAST     (1<<(1+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_MCAST     (1<<(2+MROUTE_SEC_SHIFT))
+#define MROUTE_SEC_EXTRACT_IGMP      (1<<(3+MROUTE_SEC_SHIFT))
+
+#define MROUTE_SEC_SHIFT         4
 
 /*
  * Choose the largest address possible with
@@ -62,6 +70,9 @@
 /* Address type mask indicating that netbits is part of address */
 #define MR_WITH_NETBITS          8
 
+/* Indicates than IPv4 addr was extracted from ARP packet */
+#define MR_ARP                   16
+
 struct mroute_addr {
   uint8_t len;      /* length of address */
   uint8_t unused;
@@ -72,8 +83,7 @@ struct mroute_addr {
 };
 
 /*
- * Number of bits in an address.  Should be raised for
- * IPv6.
+ * Number of bits in an address.  Should be raised for IPv6.
  */
 #define MR_HELPER_NET_LEN 32
 
@@ -89,11 +99,6 @@ struct mroute_helper {
   int net_len_refcount[MR_HELPER_NET_LEN]; /* refcount of each netlength */
 };
 
-unsigned int mroute_extract_addr_from_packet (struct mroute_addr *src,
-                                             struct mroute_addr *dest,
-                                             struct buffer *buf,
-                                             int tunnel_type);
-
 struct openvpn_sockaddr;
 
 bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
@@ -110,6 +115,13 @@ void mroute_addr_init (struct mroute_addr *addr);
 const char *mroute_addr_print (const struct mroute_addr *ma,
                               struct gc_arena *gc);
 
+#define MAPF_SUBNET            (1<<0)
+#define MAPF_IA_EMPTY_IF_UNDEF (1<<1)
+#define MAPF_SHOW_ARP          (1<<2)
+const char *mroute_addr_print_ex (const struct mroute_addr *ma,
+                                 const unsigned int flags,
+                                 struct gc_arena *gc);
+
 void mroute_addr_mask_host_bits (struct mroute_addr *ma);
 
 struct mroute_helper *mroute_helper_init (int ageable_ttl_secs);
@@ -117,6 +129,36 @@ void mroute_helper_free (struct mroute_helper *mh);
 void mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir);
 void mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir);
 
+/*
+ * Given a raw packet in buf, return the src and dest
+ * addresses of the packet.
+ */
+static inline unsigned int
+mroute_extract_addr_from_packet (struct mroute_addr *src,
+                                struct mroute_addr *dest,
+                                struct mroute_addr *esrc,
+                                struct mroute_addr *edest,
+                                const struct buffer *buf,
+                                int tunnel_type)
+{
+  unsigned int mroute_extract_addr_ipv4 (struct mroute_addr *src,
+                                        struct mroute_addr *dest,
+                                        const struct buffer *buf);
+
+  unsigned int mroute_extract_addr_ether (struct mroute_addr *src,
+                                         struct mroute_addr *dest,
+                                         struct mroute_addr *esrc,
+                                         struct mroute_addr *edest,
+                                         const struct buffer *buf);
+  unsigned int ret = 0;
+  verify_align_4 (buf);
+  if (tunnel_type == DEV_TYPE_TUN)
+    ret = mroute_extract_addr_ipv4 (src, dest, buf);
+  else if (tunnel_type == DEV_TYPE_TAP)
+    ret = mroute_extract_addr_ether (src, dest, esrc, edest, buf);
+  return ret;
+}
+
 static inline void
 mroute_helper_lock (struct mroute_helper *mh)
 {
@@ -166,11 +208,18 @@ mroute_extract_in_addr_t (struct mroute_addr *dest, const in_addr_t src)
 static inline in_addr_t
 in_addr_t_from_mroute_addr (const struct mroute_addr *addr)
 {
-  if (addr->type == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4)
+  if ((addr->type & MR_ADDR_MASK) == MR_ADDR_IPV4 && addr->netbits == 0 && addr->len == 4)
     return ntohl(*(in_addr_t*)addr->addr);
   else
     return 0;
 }
 
+static inline void
+mroute_addr_reset (struct mroute_addr *ma)
+{
+  ma->len = 0;
+  ma->type = MR_ADDR_NONE;
+}
+
 #endif /* P2MP_SERVER */
 #endif /* MROUTE_H */
diff --git a/multi.c b/multi.c
index 431ad95062e550e5ce1754cd96ddf0549ee3a194..bc3500f887add019ab32a63ec0c25e5575bff36e 100644 (file)
--- a/multi.c
+++ b/multi.c
@@ -35,6 +35,7 @@
 #include "memdbg.h"
 
 #include "forward-inline.h"
+#include "pf-inline.h"
 
 /*#define MULTI_DEBUG_EVENT_LOOP*/
 
@@ -49,6 +50,16 @@ id (struct multi_instance *mi)
 }
 #endif
 
+#ifdef MANAGEMENT_DEF_AUTH
+static void
+set_cc_config (struct multi_instance *mi, struct buffer_list *cc_config)
+{
+  if (mi->cc_config)
+    buffer_list_free (mi->cc_config);
+  mi->cc_config = cc_config;
+}
+#endif
+
 static bool
 learn_address_script (const struct multi_context *m,
                      const struct multi_instance *mi,
@@ -198,6 +209,25 @@ reap_buckets_per_pass (int n_buckets)
   return constrain_int (n_buckets / REAP_DIVISOR, REAP_MIN, REAP_MAX);
 }
 
+#ifdef MANAGEMENT_DEF_AUTH
+
+static uint32_t
+cid_hash_function (const void *key, uint32_t iv)
+{
+  const unsigned long *k = (const unsigned long *)key;
+  return (uint32_t) *k;
+}
+
+static bool
+cid_compare_function (const void *key1, const void *key2)
+{
+  const unsigned long *k1 = (const unsigned long *)key1;
+  const unsigned long *k2 = (const unsigned long *)key2;
+  return *k1 == *k2;
+}
+
+#endif
+
 /*
  * Main initialization function, init multi_context object.
  */
@@ -252,6 +282,13 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
                       mroute_addr_hash_function,
                       mroute_addr_compare_function);
 
+#ifdef MANAGEMENT_DEF_AUTH
+  m->cid_hash = hash_init (t->options.real_hash_size,
+                          0,
+                          cid_hash_function,
+                          cid_compare_function);
+#endif
+
   /*
    * This is our scheduler, for time-based wakeup
    * events.
@@ -376,6 +413,15 @@ ungenerate_prefix (struct multi_instance *mi)
   set_prefix (mi);
 }
 
+static const char *
+mi_prefix (const struct multi_instance *mi)
+{
+  if (mi && mi->msg_prefix)
+    return mi->msg_prefix;
+  else
+    return "UNDEF_I";
+}
+
 /*
  * Tell the route helper about deleted iroutes so
  * that it can update its mask of currently used
@@ -439,6 +485,11 @@ multi_client_disconnect_script (struct multi_context *m,
          
          gc_free (&gc);
        }
+#ifdef MANAGEMENT_DEF_AUTH
+      if (management)
+       management_notify_client_close (management, &mi->context.c2.mda_context, mi->context.c2.es);
+#endif
+
     }
 }
 
@@ -470,6 +521,12 @@ multi_close_instance (struct multi_context *m,
        {
          ASSERT (hash_remove (m->iter, &mi->real));
        }
+#ifdef MANAGEMENT_DEF_AUTH
+      if (mi->did_cid_hash)
+       {
+         ASSERT (hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid));
+       }
+#endif
 
       schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
 
@@ -487,6 +544,10 @@ multi_close_instance (struct multi_context *m,
       mbuf_dereference_instance (m->mbuf, mi);
     }
 
+#ifdef MANAGEMENT_DEF_AUTH
+  set_cc_config (mi, NULL);
+#endif
+
   multi_client_disconnect_script (m, mi);
 
   if (mi->did_open_context)
@@ -538,6 +599,9 @@ multi_uninit (struct multi_context *m)
          hash_free (m->hash);
          hash_free (m->vhash);
          hash_free (m->iter);
+#ifdef MANAGEMENT_DEF_AUTH
+         hash_free (m->cid_hash);
+#endif
          m->hash = NULL;
 
          schedule_free (m->schedule);
@@ -608,6 +672,13 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
     }
   mi->did_iter = true;
 
+#ifdef MANAGEMENT_DEF_AUTH
+  do {
+    mi->context.c2.mda_context.cid = m->cid_counter++;
+  } while (!hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false));
+  mi->did_cid_hash = true;
+#endif
+
   mi->context.c2.push_reply_deferred = true;
 
   if (!multi_process_post (m, mi, MPP_PRE_SELECT))
@@ -983,7 +1054,8 @@ static struct multi_instance *
 multi_learn_in_addr_t (struct multi_context *m,
                       struct multi_instance *mi,
                       in_addr_t a,
-                      int netbits) /* -1 if host route, otherwise # of network bits in address */
+                      int netbits, /* -1 if host route, otherwise # of network bits in address */
+                      bool primary)
 {
   struct openvpn_sockaddr remote_si;
   struct mroute_addr addr;
@@ -998,7 +1070,15 @@ multi_learn_in_addr_t (struct multi_context *m,
       addr.type |= MR_WITH_NETBITS;
       addr.netbits = (uint8_t) netbits;
     }
-  return multi_learn_addr (m, mi, &addr, 0);
+
+  {
+    struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
+#ifdef MANAGEMENT_DEF_AUTH
+    if (management && owner)
+      management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
+#endif
+    return owner;
+  }
 }
 
 /*
@@ -1028,7 +1108,7 @@ multi_add_iroutes (struct multi_context *m,
 
          mroute_helper_add_iroute (m->route_helper, ir);
       
-         multi_learn_in_addr_t (m, mi, ir->network, ir->netbits);
+         multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
        }
     }
   gc_free (&gc);
@@ -1255,7 +1335,7 @@ multi_client_connect_post_plugin (struct multi_context *m,
       for (i = 0; i < config.n; ++i)
        {
          if (config.list[i] && config.list[i]->value)
-           options_plugin_import (&mi->context.options,
+           options_string_import (&mi->context.options,
                                   config.list[i]->value,
                                   D_IMPORT_ERRORS|M_OPTERR,
                                   option_permissions_mask,
@@ -1276,6 +1356,46 @@ multi_client_connect_post_plugin (struct multi_context *m,
 
 #endif
 
+#ifdef MANAGEMENT_DEF_AUTH
+
+/*
+ * Called to load management-derived client-connect config
+ */
+static void
+multi_client_connect_mda (struct multi_context *m,
+                         struct multi_instance *mi,
+                         const struct buffer_list *config,
+                         unsigned int option_permissions_mask,
+                         unsigned int *option_types_found)
+{
+  if (config)
+    {
+      struct buffer_entry *be;
+  
+      for (be = config->head; be != NULL; be = be->next)
+       {
+         const char *opt = BSTR(&be->buf);
+         options_string_import (&mi->context.options,
+                                opt,
+                                D_IMPORT_ERRORS|M_OPTERR,
+                                option_permissions_mask,
+                                option_types_found,
+                                mi->context.c2.es);
+       }
+
+      /*
+       * If the --client-connect script generates a config file
+       * with an --ifconfig-push directive, it will override any
+       * --ifconfig-push directive from the --client-config-dir
+       * directory or any --ifconfig-pool dynamic address.
+       */
+      multi_select_virtual_addr (m, mi);
+      multi_set_virtual_addr_env (m, mi);
+    }
+}
+
+#endif
+
 static void
 multi_client_connect_setenv (struct multi_context *m,
                             struct multi_instance *mi)
@@ -1468,6 +1588,17 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
            cc_succeeded = false;
        }
 
+      /*
+       * Check for client-connect script left by management interface client
+       */
+#ifdef MANAGEMENT_DEF_AUTH
+      if (cc_succeeded && mi->cc_config)
+       {
+         multi_client_connect_mda (m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
+         ++cc_succeeded_count;
+       }
+#endif
+
       /*
        * Check for "disable" directive in client-config-dir file
        * or config file generated by --client-connect script.
@@ -1515,7 +1646,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
            {
              if (mi->context.c2.push_ifconfig_defined)
                {
-                 multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1);
+                 multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1, true);
                  msg (D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
                       multi_instance_string (mi, false, &gc),
                       print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
@@ -1553,6 +1684,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
       /* set flag so we don't get called again */
       mi->connection_established_flag = true;
 
+#ifdef MANAGEMENT_DEF_AUTH
+      if (management)
+       management_connection_established (management, &mi->context.c2.mda_context);
+#endif
+
       gc_free (&gc);
     }
 
@@ -1606,10 +1742,11 @@ multi_unicast (struct multi_context *m,
 /*
  * Broadcast a packet to all clients.
  */
-void
+static void
 multi_bcast (struct multi_context *m,
             const struct buffer *buf,
-            struct multi_instance *omit)
+            const struct multi_instance *sender_instance,
+            const struct mroute_addr *sender_addr)
 {
   struct hash_iterator hi;
   struct hash_element *he;
@@ -1628,8 +1765,34 @@ multi_bcast (struct multi_context *m,
       while ((he = hash_iterator_next (&hi)))
        {
          mi = (struct multi_instance *) he->value;
-         if (mi != omit && !mi->halt)
-           multi_add_mbuf (m, mi, mb);
+         if (mi != sender_instance && !mi->halt)
+           {
+#ifdef ENABLE_PF
+             if (sender_instance)
+               {
+                 if (!pf_c2c_test (&sender_instance->context, &mi->context, "bcast_c2c"))
+                   {
+                     msg (D_PF_DROPPED_BCAST, "PF: client[%s] -> client[%s] packet dropped by BCAST packet filter",
+                          mi_prefix (sender_instance),
+                          mi_prefix (mi));
+                     continue;
+                   }
+               }
+             if (sender_addr)
+               {
+                 if (!pf_addr_test (&mi->context, sender_addr, "bcast_src_addr"))
+                   {
+                     struct gc_arena gc = gc_new ();
+                     msg (D_PF_DROPPED_BCAST, "PF: addr[%s] -> client[%s] packet dropped by BCAST packet filter",
+                          mroute_addr_print_ex (sender_addr, MAPF_SHOW_ARP, &gc),
+                          mi_prefix (mi));
+                     gc_free (&gc);
+                     continue;
+                   }
+               }
+#endif
+             multi_add_mbuf (m, mi, mb);
+           }
        }
 
       hash_iterator_free (&hi);
@@ -1789,6 +1952,8 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
              /* extract packet source and dest addresses */
              mroute_flags = mroute_extract_addr_from_packet (&src,
                                                              &dest,
+                                                             NULL,
+                                                             NULL,
                                                              &c->c2.to_tun,
                                                              DEV_TYPE_TUN);
 
@@ -1811,7 +1976,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
                  if (mroute_flags & MROUTE_EXTRACT_MCAST)
                    {
                      /* for now, treat multicast as broadcast */
-                     multi_bcast (m, &c->c2.to_tun, m->pending);
+                     multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
                    }
                  else /* possible client to client routing */
                    {
@@ -1822,10 +1987,10 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
                      if (mi)
                        {
 #ifdef ENABLE_PF
-                         if (!pf_c2c_test (c, &mi->context))
+                         if (!pf_c2c_test (c, &mi->context, "tun_c2c"))
                            {
-                             msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
-                                  np (mi->msg_prefix));
+                             msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
+                                  mi_prefix (mi));
                            }
                          else
 #endif
@@ -1838,18 +2003,29 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
                    }
                }
 #ifdef ENABLE_PF
-             else if (!pf_addr_test (c, &dest))
+             if (c->c2.to_tun.len && !pf_addr_test (c, &dest, "tun_dest_addr"))
                {
-                 msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
-                      mroute_addr_print (&dest, &gc));
+                 msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
+                      mroute_addr_print_ex (&dest, MAPF_SHOW_ARP, &gc));
+                 c->c2.to_tun.len = 0;
                }
 #endif
            }
          else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP)
            {
+#ifdef ENABLE_PF
+             struct mroute_addr edest;
+             mroute_addr_reset (&edest);
+#endif
              /* extract packet source and dest addresses */
              mroute_flags = mroute_extract_addr_from_packet (&src,
                                                              &dest,
+                                                             NULL,
+#ifdef ENABLE_PF
+                                                             &edest,
+#else
+                                                             NULL,
+#endif
                                                              &c->c2.to_tun,
                                                              DEV_TYPE_TAP);
 
@@ -1862,7 +2038,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
                        {
                          if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
                            {
-                             multi_bcast (m, &c->c2.to_tun, m->pending);
+                             multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
                            }
                          else /* try client-to-client routing */
                            {
@@ -1871,12 +2047,30 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
                              /* if dest addr is a known client, route to it */
                              if (mi)
                                {
-                                 multi_unicast (m, &c->c2.to_tun, mi);
-                                 register_activity (c, BLEN(&c->c2.to_tun));
+#ifdef ENABLE_PF
+                                 if (!pf_c2c_test (c, &mi->context, "tap_c2c"))
+                                   {
+                                     msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
+                                          mi_prefix (mi));
+                                   }
+                                 else
+#endif
+                                   {
+                                     multi_unicast (m, &c->c2.to_tun, mi);
+                                     register_activity (c, BLEN(&c->c2.to_tun));
+                                   }
                                  c->c2.to_tun.len = 0;
                                }
                            }
                        }
+#ifdef ENABLE_PF
+                     if (c->c2.to_tun.len && !pf_addr_test (c, &edest, "tap_dest_addr"))
+                       {
+                         msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
+                              mroute_addr_print_ex (&edest, MAPF_SHOW_ARP, &gc));
+                         c->c2.to_tun.len = 0;
+                       }
+#endif
                    }
                  else
                    {
@@ -1918,6 +2112,20 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
       struct mroute_addr src, dest;
       const int dev_type = TUNNEL_TYPE (m->top.c1.tuntap);
 
+#ifdef ENABLE_PF
+      struct mroute_addr esrc, *e1, *e2;
+      if (dev_type == DEV_TYPE_TUN)
+       {
+         e1 = NULL;
+         e2 = &src;
+       }
+      else
+       {
+         e1 = e2 = &esrc;
+         mroute_addr_reset (&esrc);
+       }
+#endif
+
 #ifdef MULTI_DEBUG_EVENT_LOOP
       printf ("TUN -> TCP/UDP [%d]\n", BLEN (&m->top.c2.buf));
 #endif
@@ -1932,6 +2140,12 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
 
       mroute_flags = mroute_extract_addr_from_packet (&src,
                                                      &dest,
+#ifdef ENABLE_PF
+                                                     e1,
+#else
+                                                     NULL,
+#endif
+                                                     NULL,
                                                      &m->top.c2.buf,
                                                      dev_type);
 
@@ -1943,7 +2157,11 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
          if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
            {
              /* for now, treat multicast as broadcast */
-             multi_bcast (m, &m->top.c2.buf, NULL);
+#ifdef ENABLE_PF
+             multi_bcast (m, &m->top.c2.buf, NULL, e2);
+#else
+             multi_bcast (m, &m->top.c2.buf, NULL, NULL);
+#endif
            }
          else
            {
@@ -1957,10 +2175,10 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
                  set_prefix (m->pending);
 
 #ifdef ENABLE_PF
-                 if (!pf_addr_test (c, &src))
+                 if (!pf_addr_test (c, e2, "tun_tap_src_addr"))
                    {
-                     msg (D_PF, "PF: [%s] -> client packet dropped by packet filter",
-                          mroute_addr_print (&src, &gc));
+                     msg (D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter",
+                          mroute_addr_print_ex (&src, MAPF_SHOW_ARP, &gc));
                      buf_reset_len (&c->c2.buf);
                    }
                  else
@@ -2111,7 +2329,7 @@ gremlin_flood_clients (struct multi_context *m)
        ASSERT (buf_write_u8 (&buf, get_random () & 0xFF));
 
       for (i = 0; i < parm.n_packets; ++i)
-       multi_bcast (m, &buf, NULL);
+       multi_bcast (m, &buf, NULL, NULL);
 
       gc_free (&gc);
     }
@@ -2281,6 +2499,86 @@ management_delete_event (void *arg, event_t event)
 
 #endif
 
+#ifdef MANAGEMENT_DEF_AUTH
+
+static struct multi_instance *
+lookup_by_cid (struct multi_context *m, const unsigned long cid)
+{
+  if (m)
+    {
+      struct multi_instance *mi = (struct multi_instance *) hash_lookup (m->cid_hash, &cid);
+      if (mi && !mi->halt)
+       return mi;
+    }
+  return NULL;
+}
+
+static bool
+management_kill_by_cid (void *arg, const unsigned long cid)
+{
+  struct multi_context *m = (struct multi_context *) arg;
+  struct multi_instance *mi = lookup_by_cid (m, cid);
+  if (mi)
+    {
+      multi_signal_instance (m, mi, SIGTERM);
+      return true;
+    }
+  else
+    return false;
+}
+
+static bool
+management_client_auth (void *arg,
+                       const unsigned long cid,
+                       const unsigned int mda_key_id,
+                       const bool auth,
+                       const char *reason,
+                       struct buffer_list *cc_config) /* ownership transferred */
+{
+  struct multi_context *m = (struct multi_context *) arg;
+  struct multi_instance *mi = lookup_by_cid (m, cid);
+  bool cc_config_owned = true;
+  bool ret = false;
+
+  if (mi)
+    {
+      ret = tls_authenticate_key (mi->context.c2.tls_multi, mda_key_id, auth);
+      if (ret)
+       {
+         if (auth && !mi->connection_established_flag)
+           {
+             set_cc_config (mi, cc_config);
+             cc_config_owned = false;
+           }
+         if (!auth && reason)
+           msg (D_MULTI_LOW, "MULTI: connection rejected: %s", reason);
+       }
+    }
+  if (cc_config_owned && cc_config)
+    buffer_list_free (cc_config);
+  return ret;
+}
+#endif
+
+#ifdef MANAGEMENT_PF
+static bool
+management_client_pf (void *arg,
+                     const unsigned long cid,
+                     struct buffer_list *pf_config) /* ownership transferred */
+{
+  struct multi_context *m = (struct multi_context *) arg;
+  struct multi_instance *mi = lookup_by_cid (m, cid);
+  bool ret = false;
+
+  if (mi && pf_config)
+    ret = pf_load_from_buffer_list (&mi->context, pf_config);
+
+  if (pf_config)
+    buffer_list_free (pf_config);
+  return ret;
+}
+#endif
+
 void
 init_management_callback_multi (struct multi_context *m)
 {
@@ -2295,6 +2593,13 @@ init_management_callback_multi (struct multi_context *m)
       cb.kill_by_cn = management_callback_kill_by_cn;
       cb.kill_by_addr = management_callback_kill_by_addr;
       cb.delete_event = management_delete_event;
+#ifdef MANAGEMENT_DEF_AUTH
+      cb.kill_by_cid = management_kill_by_cid;
+      cb.client_auth = management_client_auth;
+#endif
+#ifdef MANAGEMENT_PF
+      cb.client_pf = management_client_pf;
+#endif
       management_set_callback (management, &cb);
     }
 #endif
diff --git a/multi.h b/multi.h
index c796c23624d7243130c9dbffe041c988b71ecfae..3abfeaea3d2f02f4a6c7e74473cc7c47bd9774b7 100644 (file)
--- a/multi.h
+++ b/multi.h
@@ -77,6 +77,10 @@ struct multi_instance {
   bool did_open_context;
   bool did_real_hash;
   bool did_iter;
+#ifdef MANAGEMENT_DEF_AUTH
+  bool did_cid_hash;
+  struct buffer_list *cc_config;
+#endif
   bool connection_established_flag;
   bool did_iroutes;
 
@@ -111,6 +115,11 @@ struct multi_context {
   int tcp_queue_limit;
   int status_file_version;
 
+#ifdef MANAGEMENT_DEF_AUTH
+  struct hash *cid_hash;
+  unsigned long cid_counter;
+#endif
+
   struct multi_instance *pending;
   struct multi_instance *earliest_wakeup;
   struct multi_instance **mpp_touched;
@@ -143,10 +152,6 @@ void tunnel_server (struct context *top);
 
 const char *multi_instance_string (const struct multi_instance *mi, bool null, struct gc_arena *gc);
 
-void multi_bcast (struct multi_context *m,
-                 const struct buffer *buf,
-                 struct multi_instance *omit);
-
 /*
  * Called by mtcp.c, mudp.c, or other (to be written) protocol drivers
  */
index 8a239f9fccf2d048c188d897abed65a644caa8cf..69d8f8f3dc6dada826c62d2deb9c7a16f7b365ef 100644 (file)
--- a/openvpn.8
+++ b/openvpn.8
@@ -179,6 +179,8 @@ openvpn \- secure IP tunnel daemon.
 [\ \fB\-\-log\fR\ \fIfile\fR\ ]
 [\ \fB\-\-suppress-timestamps\fR\ ]
 [\ \fB\-\-lport\fR\ \fIport\fR\ ]
+[\ \fB\-\-management\-client\-auth\fR\ ]
+[\ \fB\-\-management\-client\-pf\fR\ ]
 [\ \fB\-\-management\-forget\-disconnect\fR\ ]
 [\ \fB\-\-management\-hold\fR\ ]
 [\ \fB\-\-management\-log\-cache\fR\ \fIn\fR\ ]
@@ -2357,6 +2359,19 @@ lines of log file history for usage
 by the management channel.
 .\"*********************************************************
 .TP
+.B --management-client-auth
+Gives management interface client the responsibility
+to authenticate clients after their client certificate
+has been verified.  See management-notes.txt in OpenVPN
+distribution for detailed notes.
+.\"*********************************************************
+.TP
+.B --management-client-pf
+Management interface clients must specify a packet
+filter file for each connecting client.  See management-notes.txt
+in OpenVPN distribution for detailed notes.
+.\"*********************************************************
+.TP
 .B --plugin module-pathname [init-string]
 Load plug-in module from the file
 .B module-pathname,
index f637cd53a0e177208a62038a2d91419998abddf2..1ffc44c8a569c392d66e37b77fca3fe761b64d90 100644 (file)
--- a/openvpn.h
+++ b/openvpn.h
@@ -437,6 +437,10 @@ struct context_2
 #ifdef ENABLE_PF
   struct pf_context pf;
 #endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+  struct man_def_auth_context mda_context;
+#endif
 };
 
 /*
index e355e168dcd668bac67eddf04a532950e8bbf530..5328efbe4f294fb5f0cd8f52f7cb3e7e75e15d8e 100644 (file)
--- a/options.c
+++ b/options.c
@@ -316,6 +316,15 @@ static const char usage_message[] =
   "                                 event occurs.\n"
   "--management-log-cache n : Cache n lines of log file history for usage\n"
   "                  by the management channel.\n"
+#ifdef MANAGEMENT_DEF_AUTH
+  "--management-client-auth : gives management interface client the responsibility\n"
+  "                           to authenticate clients after their client certificate\n"
+  "                          has been verified.\n"
+#endif
+#ifdef MANAGEMENT_PF
+  "--management-client-pf : management interface clients must specify a packet\n"
+  "                         filter file for each connecting client.\n"
+#endif
 #endif
 #ifdef ENABLE_PLUGIN
   "--plugin m [str]: Load plug-in module m passing str as an argument\n"
@@ -1195,12 +1204,8 @@ show_settings (const struct options *o)
   SHOW_STR (management_user_pass);
   SHOW_INT (management_log_history_cache);
   SHOW_INT (management_echo_buffer_size);
-  SHOW_BOOL (management_query_passwords);
-  SHOW_BOOL (management_hold);
-  SHOW_BOOL (management_client);
-  SHOW_BOOL (management_signal);
-  SHOW_BOOL (management_forget_disconnect);
   SHOW_STR (management_write_peer_info_file);
+  SHOW_INT (management_flags);
 #endif
 #ifdef ENABLE_PLUGIN
   if (o->plugin_list)
@@ -1525,8 +1530,7 @@ options_postprocess (struct options *options, bool first_time)
    */
 #ifdef ENABLE_MANAGEMENT
   if (!options->management_addr &&
-      (options->management_query_passwords || options->management_hold || options->management_signal
-       || options->management_forget_disconnect || options->management_client
+      (options->management_flags
        || options->management_write_peer_info_file
        || options->management_log_history_cache != defaults.management_log_history_cache))
     msg (M_USAGE, "--management is not specified, however one or more options which modify the behavior of --management were specified");
@@ -1672,12 +1676,15 @@ options_postprocess (struct options *options, bool first_time)
       if (options->key_method != 2)
        msg (M_USAGE, "--mode server requires --key-method 2");
 
-      if (PLUGIN_OPTION_LIST (options) == NULL)
        {
-         if (options->client_cert_not_required && !options->auth_user_pass_verify_script)
-           msg (M_USAGE, "--client-cert-not-required must be used with an --auth-user-pass-verify script");
-         if (options->username_as_common_name && !options->auth_user_pass_verify_script)
-           msg (M_USAGE, "--username-as-common-name must be used with an --auth-user-pass-verify script");
+         const bool ccnr = (options->auth_user_pass_verify_script
+                            || PLUGIN_OPTION_LIST (options)
+                            || MAN_CLIENT_AUTH_ENABLED (options));
+         const char *postfix = "must be used with --management-client-auth, an --auth-user-pass-verify script, or plugin";
+         if (options->client_cert_not_required && !ccnr)
+           msg (M_USAGE, "--client-cert-not-required %s", postfix);
+         if (options->username_as_common_name && !ccnr)
+           msg (M_USAGE, "--username-as-common-name %s", postfix);
        }
     }
   else
@@ -2983,9 +2990,7 @@ options_server_import (struct options *o,
                    es);
 }
 
-#ifdef ENABLE_PLUGIN
-
-void options_plugin_import (struct options *options,
+void options_string_import (struct options *options,
                            const char *config,
                            const int msglevel,
                            const unsigned int permission_mask,
@@ -2995,8 +3000,6 @@ void options_plugin_import (struct options *options,
   read_config_string (options, config, msglevel, permission_mask, option_types_found, es);
 }
 
-#endif
-
 #if P2MP
 
 #define VERIFY_PERMISSION(mask) { if (!verify_permission(p[0], (mask), permission_mask, option_types_found, msglevel)) goto err; }
@@ -3144,29 +3147,43 @@ add_option (struct options *options,
   else if (streq (p[0], "management-query-passwords"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
-      options->management_query_passwords = true;
+      options->management_flags |= MF_QUERY_PASSWORDS;
     }
   else if (streq (p[0], "management-hold"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
-      options->management_hold = true;
+      options->management_flags |= MF_HOLD;
     }
   else if (streq (p[0], "management-signal"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
-      options->management_signal = true;
+      options->management_flags |= MF_SIGNAL;
     }
   else if (streq (p[0], "management-forget-disconnect"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
-      options->management_forget_disconnect = true;
+      options->management_flags |= MF_FORGET_DISCONNECT;
     }
   else if (streq (p[0], "management-client"))
     {
       VERIFY_PERMISSION (OPT_P_GENERAL);
-      options->management_client = true;
+      options->management_flags |= MF_CONNECT_AS_CLIENT;
       options->management_write_peer_info_file = p[1];
     }
+#ifdef MANAGEMENT_DEF_AUTH
+  else if (streq (p[0], "management-client-auth"))
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->management_flags |= MF_CLIENT_AUTH;
+    }
+#endif
+#ifdef MANAGEMENT_PF
+  else if (streq (p[0], "management-client-pf"))
+    {
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      options->management_flags |= (MF_CLIENT_PF | MF_CLIENT_AUTH);
+    }
+#endif
   else if (streq (p[0], "management-log-cache") && p[1])
     {
       int cache;
index 607df0f31796ef2712b41eeeb5d9b679ce0d6829..39f2ac682332317ed1af74b1e09ff23ecac37a9a 100644 (file)
--- a/options.h
+++ b/options.h
@@ -281,12 +281,10 @@ struct options
   int management_log_history_cache;
   int management_echo_buffer_size;
   int management_state_buffer_size;
-  bool management_query_passwords;
-  bool management_hold;
-  bool management_signal;
-  bool management_forget_disconnect;
-  bool management_client;
   const char *management_write_peer_info_file;
+
+  /* Mask of MF_ values of manage.h */
+  unsigned int management_flags;
 #endif
 
 #ifdef ENABLE_PLUGIN
@@ -537,6 +535,12 @@ struct options
 #define PLUGIN_OPTION_LIST(opt) (NULL)
 #endif
 
+#ifdef MANAGEMENT_DEF_AUTH
+#define MAN_CLIENT_AUTH_ENABLED(opt) ((opt)->management_flags & MF_CLIENT_AUTH)
+#else
+#define MAN_CLIENT_AUTH_ENABLED(opt) (false)
+#endif
+
 void parse_argv (struct options *options,
                 const int argc,
                 char *argv[],
@@ -632,9 +636,7 @@ const char *auth_retry_print (void);
 
 #endif
 
-#ifdef ENABLE_PLUGIN
-
-void options_plugin_import (struct options *options,
+void options_string_import (struct options *options,
                            const char *config,
                            const int msglevel,
                            const unsigned int permission_mask,
@@ -642,5 +644,3 @@ void options_plugin_import (struct options *options,
                            struct env_set *es);
 
 #endif
-
-#endif
diff --git a/pf-inline.h b/pf-inline.h
new file mode 100644 (file)
index 0000000..617f0bf
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  OpenVPN -- An application to securely tunnel IP networks
+ *             over a single TCP/UDP port, with support for SSL/TLS-based
+ *             session authentication and key exchange,
+ *             packet encryption, packet authentication, and
+ *             packet compression.
+ *
+ *  Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2
+ *  as published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if defined(ENABLE_PF) && !defined(PF_INLINE_H)
+#define PF_INLINE_H
+
+/*
+ * Inline functions
+ */
+
+#define PCT_SRC  1
+#define PCT_DEST 2
+static inline bool
+pf_c2c_test (const struct context *src, const struct context *dest, const char *prefix)
+{
+  bool pf_cn_test (struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix);
+  return  (!src->c2.pf.enabled  || pf_cn_test (src->c2.pf.pfs,  dest->c2.tls_multi, PCT_DEST, prefix))
+       && (!dest->c2.pf.enabled || pf_cn_test (dest->c2.pf.pfs, src->c2.tls_multi,  PCT_SRC,  prefix));
+}
+
+static inline bool
+pf_addr_test (const struct context *src, const struct mroute_addr *dest, const char *prefix)
+{
+  bool pf_addr_test_dowork (const struct context *src, const struct mroute_addr *dest, const char *prefix);
+
+  if (src->c2.pf.enabled)
+    return pf_addr_test_dowork (src, dest, prefix);
+  else
+    return true;
+}
+
+static inline bool
+pf_kill_test (const struct pf_set *pfs)
+{
+  return pfs->kill;
+}
+
+#endif
diff --git a/pf.c b/pf.c
index 87d7c3bf4155568bc714d42844c2e54341c9c868..b7ed4c87ac773f0d67cb180242fb483219ec96ec 100644 (file)
--- a/pf.c
+++ b/pf.c
@@ -32,6 +32,8 @@
 
 #include "memdbg.h"
 
+#include "pf-inline.h"
+
 static void
 pf_destroy (struct pf_set *pfs)
 {
@@ -64,7 +66,7 @@ pf_destroy (struct pf_set *pfs)
 }
 
 static bool
-add_client (const char *line, const char *fn, const int line_num, struct pf_cn_elem ***next, const bool exclude)
+add_client (const char *line, const char *prefix, const int line_num, struct pf_cn_elem ***next, const bool exclude)
 {
   struct pf_cn_elem *e;
   ALLOC_OBJ_CLEAR (e, struct pf_cn_elem);
@@ -76,34 +78,44 @@ add_client (const char *line, const char *fn, const int line_num, struct pf_cn_e
 }
 
 static bool
-add_subnet (const char *line, const char *fn, const int line_num, struct pf_subnet ***next, const bool exclude)
+add_subnet (const char *line, const char *prefix, const int line_num, struct pf_subnet ***next, const bool exclude)
 {
   struct in_addr network;
   in_addr_t netmask = 0;
-  int netbits = 32;
-  char *div = strchr (line, '/');
 
-  if (div)
+  if (strcmp (line, "unknown"))
     {
-      *div++ = '\0';
-      if (sscanf (div, "%d", &netbits) != 1)
+      int netbits = 32;
+      char *div = strchr (line, '/');
+
+      if (div)
        {
-         msg (D_PF, "PF: %s/%d: bad '/n' subnet specifier: '%s'", fn, line_num, div);
-         return false;
+         *div++ = '\0';
+         if (sscanf (div, "%d", &netbits) != 1)
+           {
+             msg (D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: '%s'", prefix, line_num, div);
+             return false;
+           }
+         if (netbits < 0 || netbits > 32)
+           {
+             msg (D_PF_INFO, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", prefix, line_num, div);
+             return false;
+           }
        }
-      if (netbits < 0 || netbits > 32)
+
+      if (openvpn_inet_aton (line, &network) != OIA_IP)
        {
-         msg (D_PF, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", fn, line_num, div);
+         msg (D_PF_INFO, "PF: %s/%d: bad network address: '%s'", prefix, line_num, line);
          return false;
        }
+      netmask = netbits_to_netmask (netbits);
     }
-
-  if (openvpn_inet_aton (line, &network) != OIA_IP)
+  else
     {
-      msg (D_PF, "PF: %s/%d: bad network address: '%s'", fn, line_num, line);
-      return false;
+      /* match special "unknown" tag for addresses unrecognized by mroute */
+      network.s_addr = htonl(0);
+      netmask = ~0;
     }
-  netmask = netbits_to_netmask (netbits);
 
   {
     struct pf_subnet *e;
@@ -130,7 +142,7 @@ cn_compare_function (const void *key1, const void *key2)
 }
 
 static bool
-genhash (struct pf_cn_set *cns, const char *fn, const int n_clients)
+genhash (struct pf_cn_set *cns, const char *prefix, const int n_clients)
 {
   struct pf_cn_elem *e;
   bool status = true;
@@ -143,7 +155,7 @@ genhash (struct pf_cn_set *cns, const char *fn, const int n_clients)
     {
       if (!hash_add (cns->hash_table, e->rule.cn, &e->rule, false))
        {
-         msg (D_PF, "PF: %s: duplicate common name in [clients] section: '%s'", fn, e->rule.cn);
+         msg (D_PF_INFO, "PF: %s: duplicate common name in [clients] section: '%s'", prefix, e->rule.cn);
          status = false;
        }
     }
@@ -152,7 +164,7 @@ genhash (struct pf_cn_set *cns, const char *fn, const int n_clients)
 }
 
 static struct pf_set *
-pf_init (const char *fn)
+pf_init (const struct buffer_list *bl, const char *prefix, const bool allow_kill)
 {
 # define MODE_UNDEF   0
 # define MODE_CLIENTS 1
@@ -163,18 +175,19 @@ pf_init (const char *fn)
   int n_subnets = 0;
   int n_errors = 0;
   struct pf_set *pfs = NULL;
-  char line[256];
+  char line[PF_MAX_LINE_LEN];
 
   ALLOC_OBJ_CLEAR (pfs, struct pf_set);
-  FILE *fp = fopen (fn, "r");
-  if (fp)
+  if (bl)
     {
       struct pf_cn_elem **cl = &pfs->cns.list;
       struct pf_subnet **sl = &pfs->sns.list;
+      struct buffer_entry *be;
 
-      while (fgets (line, sizeof (line), fp) != NULL)
+      for (be = bl->head; be != NULL; be = be->next)
        {
          ++line_num;
+         strncpynt (line, BSTR(&be->buf), sizeof(line));
          rm_trailing_chars (line, "\r\n\t ");
          if (line[0] == '\0' || line[0] == '#')
            ;
@@ -184,19 +197,19 @@ pf_init (const char *fn)
 
              if (line[1] =='\0')
                {
-                 msg (D_PF, "PF: %s/%d: no data after +/-: '%s'", fn, line_num, line);
+                 msg (D_PF_INFO, "PF: %s/%d: no data after +/-: '%s'", prefix, line_num, line);
                  ++n_errors;
                }
              else if (mode == MODE_CLIENTS)
                {
-                 if (add_client (&line[1], fn, line_num, &cl, exclude))
+                 if (add_client (&line[1], prefix, line_num, &cl, exclude))
                    ++n_clients;
                  else
                    ++n_errors;
                }
              else if (mode == MODE_SUBNETS)
                {
-                 if (add_subnet (&line[1], fn, line_num, &sl, exclude))
+                 if (add_subnet (&line[1], prefix, line_num, &sl, exclude))
                    ++n_subnets;
                  else
                    ++n_errors;
@@ -232,41 +245,40 @@ pf_init (const char *fn)
                }
              else if (!strcasecmp (line, "[end]"))
                goto done;
-             else if (!strcasecmp (line, "[kill]"))
+             else if (allow_kill && !strcasecmp (line, "[kill]"))
                goto kill;
              else
                {
                  mode = MODE_UNDEF;
-                 msg (D_PF, "PF: %s/%d unknown tag: '%s'", fn, line_num, line);
+                 msg (D_PF_INFO, "PF: %s/%d unknown tag: '%s'", prefix, line_num, line);
                  ++n_errors;
                }
            }
          else
            {
-             msg (D_PF, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", fn, line_num, line);
+             msg (D_PF_INFO, "PF: %s/%d line must begin with '+', '-', or '[' : '%s'", prefix, line_num, line);
              ++n_errors;
            }
        }
       ++n_errors;
-      msg (D_PF, "PF: %s: missing [end]", fn);
+      msg (D_PF_INFO, "PF: %s: missing [end]", prefix);
     }
   else
     {
-      msg (D_PF|M_ERRNO, "PF: %s: cannot open", fn);
+      msg (D_PF_INFO, "PF: %s: cannot open", prefix);
       ++n_errors;
     }
 
  done:
-  if (fp)
+  if (bl)
     {
-      fclose (fp);
       if (!n_errors)
        {
-         if (!genhash (&pfs->cns, fn, n_clients))
+         if (!genhash (&pfs->cns, prefix, n_clients))
            ++n_errors;
        }
       if (n_errors)
-       msg (D_PF, "PF: %s rejected due to %d error(s)", fn, n_errors);
+       msg (D_PF_INFO, "PF: %s rejected due to %d error(s)", prefix, n_errors);
     }
   if (n_errors)
     {
@@ -276,15 +288,32 @@ pf_init (const char *fn)
   return pfs;
   
  kill:
-  if (fp)
-    fclose (fp);
   pf_destroy (pfs);
   ALLOC_OBJ_CLEAR (pfs, struct pf_set);
   pfs->kill = true;
   return pfs;
 }
 
-#if PF_DEBUG >= 1
+#ifdef PLUGIN_PF
+static struct pf_set *
+pf_init_from_file (const char *fn)
+{
+  struct buffer_list *bl = buffer_list_file (fn, PF_MAX_LINE_LEN);
+  if (bl)
+    {
+      struct pf_set *pfs = pf_init (bl, fn, true);
+      buffer_list_free (bl);
+      return pfs;
+    }
+  else
+    {
+      msg (D_PF_INFO|M_ERRNO, "PF: %s: cannot open", fn);
+      return NULL;
+    }
+}
+#endif
+
+#ifdef ENABLE_DEBUG
 
 static const char *
 drop_accept (const bool accept)
@@ -292,31 +321,46 @@ drop_accept (const bool accept)
   return accept ? "ACCEPT" : "DROP"; 
 }
 
-#endif
-
-#if PF_DEBUG >= 2
+static const char *
+pct_name (const int type)
+{
+  switch (type)
+    {
+    case PCT_SRC:
+      return "SRC";
+    case PCT_DEST:
+      return "DEST";
+    default:
+      return "???";
+    }
+}
 
 static void
 pf_cn_test_print (const char *prefix,
+                 const int type,
+                 const char *prefix2,
                  const char *cn,
                  const bool allow,
                  const struct pf_cn *rule)
 {
   if (rule)
     {
-      msg (D_PF, "PF: %s %s %s rule=[%s %s]",
-          prefix, cn, drop_accept (allow),
+      dmsg (D_PF_DEBUG, "PF: %s/%s/%s %s %s rule=[%s %s]",
+          prefix, prefix2, pct_name (type),
+          cn, drop_accept (allow),
           rule->cn, drop_accept (!rule->exclude));
     }
   else
     {
-      msg (D_PF, "PF: %s %s %s",
-          prefix, cn, drop_accept (allow));
+      dmsg (D_PF_DEBUG, "PF: %s/%s/%s %s %s",
+          prefix, prefix2, pct_name (type),
+          cn, drop_accept (allow));
     }
 }
 
 static void
 pf_addr_test_print (const char *prefix,
+                   const char *prefix2,
                    const struct context *src,
                    const struct mroute_addr *dest,
                    const bool allow,
@@ -325,10 +369,11 @@ pf_addr_test_print (const char *prefix,
   struct gc_arena gc = gc_new ();
   if (rule)
     {
-      msg (D_PF, "PF: %s %s %s %s rule=[%s/%s %s]",
+      dmsg (D_PF_DEBUG, "PF: %s/%s %s %s %s rule=[%s/%s %s]",
           prefix,
+          prefix2,
           tls_common_name (src->c2.tls_multi, false),
-          mroute_addr_print (dest, &gc),
+          mroute_addr_print_ex (dest, MAPF_SHOW_ARP, &gc),
           drop_accept (allow),
           print_in_addr_t (rule->network, 0, &gc),
           print_in_addr_t (rule->netmask, 0, &gc),
@@ -336,10 +381,11 @@ pf_addr_test_print (const char *prefix,
     }
   else
     {
-      msg (D_PF, "PF: %s %s %s %s",
+      dmsg (D_PF_DEBUG, "PF: %s/%s %s %s %s",
           prefix,
+          prefix2,
           tls_common_name (src->c2.tls_multi, false),
-          mroute_addr_print (dest, &gc),
+          mroute_addr_print_ex (dest, MAPF_SHOW_ARP, &gc),
           drop_accept (allow));
     }
   gc_free (&gc);
@@ -357,8 +403,8 @@ lookup_cn_rule (struct hash *h, const char *cn, const uint32_t cn_hash)
     return NULL;
 }
 
-static inline bool
-cn_test (struct pf_set *pfs, const struct tls_multi *tm)
+bool
+pf_cn_test (struct pf_set *pfs, const struct tls_multi *tm, const int type, const char *prefix)
 {
   if (!pfs->kill)
     {
@@ -369,8 +415,9 @@ cn_test (struct pf_set *pfs, const struct tls_multi *tm)
          const struct pf_cn *rule = lookup_cn_rule (pfs->cns.hash_table, cn, cn_hash);
          if (rule)
            {
-#if PF_DEBUG >= 2
-             pf_cn_test_print ("PF_CN_MATCH", cn, !rule->exclude, rule);
+#ifdef ENABLE_DEBUG
+             if (check_debug_level (D_PF_DEBUG))
+               pf_cn_test_print ("PF_CN_MATCH", type, prefix, cn, !rule->exclude, rule);
 #endif
              if (!rule->exclude)
                return true;
@@ -379,8 +426,9 @@ cn_test (struct pf_set *pfs, const struct tls_multi *tm)
            }
          else
            {
-#if PF_DEBUG >= 2
-             pf_cn_test_print ("PF_CN_DEFAULT", cn, pfs->cns.default_allow, NULL);
+#ifdef ENABLE_DEBUG
+             if (check_debug_level (D_PF_DEBUG))
+               pf_cn_test_print ("PF_CN_DEFAULT", type, prefix, cn, pfs->cns.default_allow, NULL);
 #endif
              if (pfs->cns.default_allow)
                return true;
@@ -389,59 +437,50 @@ cn_test (struct pf_set *pfs, const struct tls_multi *tm)
            }
        }
     }
-#if PF_DEBUG >= 2
-  pf_cn_test_print ("PF_CN_FAULT", tls_common_name (tm, false), false, NULL);
+#ifdef ENABLE_DEBUG
+  if (check_debug_level (D_PF_DEBUG))
+    pf_cn_test_print ("PF_CN_FAULT", type, prefix, tls_common_name (tm, false), false, NULL);
 #endif
   return false;
 }
 
 bool
-pf_c2c_test (const struct context *src, const struct context *dest)
+pf_addr_test_dowork (const struct context *src, const struct mroute_addr *dest, const char *prefix)
 {
-  return  (!src->c2.pf.filename  || cn_test (src->c2.pf.pfs,  dest->c2.tls_multi))
-       && (!dest->c2.pf.filename || cn_test (dest->c2.pf.pfs, src->c2.tls_multi));
-}
-
-bool
-pf_addr_test (const struct context *src, const struct mroute_addr *dest)
-{
-  if (src->c2.pf.filename)
+  struct pf_set *pfs = src->c2.pf.pfs;
+  if (pfs && !pfs->kill)
     {
-      struct pf_set *pfs = src->c2.pf.pfs;
-      if (pfs && !pfs->kill)
+      const in_addr_t addr = in_addr_t_from_mroute_addr (dest);
+      const struct pf_subnet *se = pfs->sns.list;
+      while (se)
        {
-         const in_addr_t addr = in_addr_t_from_mroute_addr (dest);
-         const struct pf_subnet *se = pfs->sns.list;
-         while (se)
+         if ((addr & se->rule.netmask) == se->rule.network)
            {
-             if ((addr & se->rule.netmask) == se->rule.network)
-               {
-#if PF_DEBUG >= 2
-                 pf_addr_test_print ("PF_ADDR_MATCH", src, dest, !se->rule.exclude, &se->rule);
+#ifdef ENABLE_DEBUG
+             if (check_debug_level (D_PF_DEBUG))
+               pf_addr_test_print ("PF_ADDR_MATCH", prefix, src, dest, !se->rule.exclude, &se->rule);
 #endif
-                 return !se->rule.exclude;
-               }
-             se = se->next;
+             return !se->rule.exclude;
            }
-#if PF_DEBUG >= 2
-         pf_addr_test_print ("PF_ADDR_DEFAULT", src, dest, pfs->sns.default_allow, NULL);
-#endif
-         return pfs->sns.default_allow;
+         se = se->next;
        }
-      else
-       {
-#if PF_DEBUG >= 2
-         pf_addr_test_print ("PF_ADDR_FAULT", src, dest, false, NULL);
+#ifdef ENABLE_DEBUG
+      if (check_debug_level (D_PF_DEBUG))
+       pf_addr_test_print ("PF_ADDR_DEFAULT", prefix, src, dest, pfs->sns.default_allow, NULL);
 #endif
-         return false;
-       }
+      return pfs->sns.default_allow;
     }
   else
     {
-      return true;
+#ifdef ENABLE_DEBUG
+      if (check_debug_level (D_PF_DEBUG))
+       pf_addr_test_print ("PF_ADDR_FAULT", prefix, src, dest, false, NULL);
+#endif
+      return false;
     }
 }
 
+#ifdef PLUGIN_PF
 void
 pf_check_reload (struct context *c)
 {
@@ -450,14 +489,16 @@ pf_check_reload (struct context *c)
   const int wakeup_transition = 60;
   bool reloaded = false;
 
-  if (c->c2.pf.filename && event_timeout_trigger (&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT))
+  if (c->c2.pf.enabled
+      && c->c2.pf.filename
+      && event_timeout_trigger (&c->c2.pf.reload, &c->c2.timeval, ETT_DEFAULT))
     {
       struct stat s;
       if (!stat (c->c2.pf.filename, &s))
        {
          if (s.st_mtime > c->c2.pf.file_last_mod)
            {
-             struct pf_set *pfs = pf_init (c->c2.pf.filename);
+             struct pf_set *pfs = pf_init_from_file (c->c2.pf.filename);
              if (pfs)
                {
                  if (c->c2.pf.pfs)
@@ -482,16 +523,35 @@ pf_check_reload (struct context *c)
        c->c2.pf.n_check_reload++;
       }
     }
-#if PF_DEBUG >= 1
-  if (reloaded)
-    pf_context_print (&c->c2.pf, "pf_check_reload", M_INFO);
+#ifdef ENABLE_DEBUG
+  if (reloaded && check_debug_level (D_PF_DEBUG))
+    pf_context_print (&c->c2.pf, "pf_check_reload", D_PF_DEBUG);
 #endif
 }
+#endif
+
+#ifdef MANAGEMENT_PF
+bool
+pf_load_from_buffer_list (struct context *c, const struct buffer_list *config)
+{
+  struct pf_set *pfs = pf_init (config, "[SERVER-PF]", false);
+  if (pfs)
+    {
+      if (c->c2.pf.pfs)
+       pf_destroy (c->c2.pf.pfs);
+      c->c2.pf.pfs = pfs;
+      return true;
+    }
+  else
+    return false;
+}
+#endif
 
 void
 pf_init_context (struct context *c)
 {
   struct gc_arena gc = gc_new ();
+#ifdef PLUGIN_PF
   if (plugin_defined (c->plugins, OPENVPN_PLUGIN_ENABLE_PF))
     {
       const char *pf_file = create_temp_filename (c->options.tmp_dir, "pf", &gc);
@@ -502,8 +562,10 @@ pf_init_context (struct context *c)
        {
          event_timeout_init (&c->c2.pf.reload, 1, now);
          c->c2.pf.filename = string_alloc (pf_file, NULL);
-#if PF_DEBUG >= 1
-         pf_context_print (&c->c2.pf, "pf_init_context", M_INFO);
+         c->c2.pf.enabled = true;
+#ifdef ENABLE_DEBUG
+         if (check_debug_level (D_PF_DEBUG))
+           pf_context_print (&c->c2.pf, "pf_init_context#1", D_PF_DEBUG);
 #endif
        }
       else
@@ -511,22 +573,35 @@ pf_init_context (struct context *c)
          msg (M_WARN, "WARNING: OPENVPN_PLUGIN_ENABLE_PF disabled");
        }
     }
+#endif
+#ifdef MANAGEMENT_PF
+  if (!c->c2.pf.enabled && management_enable_pf (management))
+    {
+      c->c2.pf.enabled = true;
+#ifdef ENABLE_DEBUG
+      if (check_debug_level (D_PF_DEBUG))
+       pf_context_print (&c->c2.pf, "pf_init_context#2", D_PF_DEBUG);
+#endif
+    }
+#endif
   gc_free (&gc);
 }
 
 void
 pf_destroy_context (struct pf_context *pfc)
 {
+#ifdef PLUGIN_PF
   if (pfc->filename)
     {
       delete_file (pfc->filename);
       free (pfc->filename);
     }
+#endif
   if (pfc->pfs)
     pf_destroy (pfc->pfs);
 }
 
-#if PF_DEBUG >= 1
+#ifdef ENABLE_DEBUG
 
 static void
 pf_subnet_set_print (const struct pf_subnet_set *s, const int lev)
@@ -613,10 +688,13 @@ pf_context_print (const struct pf_context *pfc, const char *prefix, const int le
   msg (lev, "----- %s : struct pf_context -----", prefix);
   if (pfc)
     {
+      msg (lev, "enabled=%d", pfc->enabled);
+#ifdef PLUGIN_PF
       msg (lev, "filename='%s'", np(pfc->filename));
       msg (lev, "file_last_mod=%u", (unsigned int)pfc->file_last_mod);
       msg (lev, "n_check_reload=%u", pfc->n_check_reload);
       msg (lev, "reload=[%d,%u,%u]", pfc->reload.defined, pfc->reload.n, (unsigned int)pfc->reload.last);
+#endif
       pf_set_print (pfc->pfs, lev);
     }
   msg (lev, "--------------------");
diff --git a/pf.h b/pf.h
index 754db8afd3f19018a2b8e21316f16f64fabce14f..b001bc88b0983fb4bedf7706aadeb34d917cfdc6 100644 (file)
--- a/pf.h
+++ b/pf.h
@@ -30,7 +30,7 @@
 #include "list.h"
 #include "mroute.h"
 
-#define PF_DEBUG 0
+#define PF_MAX_LINE_LEN 256
 
 struct context;
 
@@ -73,30 +73,29 @@ struct pf_set {
 };
 
 struct pf_context {
+  bool enabled;
+  struct pf_set *pfs;
+#ifdef PLUGIN_PF
   char *filename;
   time_t file_last_mod;
   unsigned int n_check_reload;
   struct event_timeout reload;
-  struct pf_set *pfs;
+#endif
 };
 
 void pf_init_context (struct context *c);
 
 void pf_destroy_context (struct pf_context *pfc);
 
+#ifdef PLUGIN_PF
 void pf_check_reload (struct context *c);
+#endif
 
-bool pf_c2c_test (const struct context *src, const struct context *dest);
-
-bool pf_addr_test (const struct context *src, const struct mroute_addr *dest);
-
-static inline bool
-pf_kill_test (const struct pf_set *pfs)
-{
-  return pfs->kill;
-}
+#ifdef MANAGEMENT_PF
+bool pf_load_from_buffer_list (struct context *c, const struct buffer_list *config);
+#endif
 
-#if PF_DEBUG >= 1
+#ifdef ENABLE_DEBUG
 void pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev);
 #endif
 
diff --git a/proto.h b/proto.h
index 77d9c0dbe7f81981dd3edd35ee0267df8ac938e0..2be24ef0f24669cf8165bcbc890f855efe17a10a 100644 (file)
--- a/proto.h
+++ b/proto.h
@@ -28,6 +28,8 @@
 #include "common.h"
 #include "buffer.h"
 
+#pragma pack(1)
+
 /*
  * Tunnel types
  */
@@ -62,6 +64,24 @@ struct openvpn_ethhdr
   uint16_t proto;                     /* packet type ID field */
 };
 
+struct openvpn_arp {
+# define ARP_MAC_ADDR_TYPE 0x0001
+  uint16_t mac_addr_type;       // 0x0001
+
+  uint16_t proto_addr_type;     // 0x0800
+  uint8_t  mac_addr_size;       // 0x06
+  uint8_t  proto_addr_size;     // 0x04
+
+# define ARP_REQUEST 0x0001
+# define ARP_REPLY   0x0002
+  uint16_t arp_command;         // 0x0001 for ARP request, 0x0002 for ARP reply
+
+  uint8_t   mac_src[OPENVPN_ETH_ALEN];
+  in_addr_t ip_src;
+  uint8_t   mac_dest[OPENVPN_ETH_ALEN];
+  in_addr_t ip_dest;
+};
+
 struct openvpn_iphdr {
 # define OPENVPN_IPH_GET_VER(v) (((v) >> 4) & 0x0F)
 # define OPENVPN_IPH_GET_LEN(v) (((v) & 0x0F) << 2)
@@ -129,6 +149,8 @@ struct openvpn_tcphdr {
 #define        OPENVPN_TCPOPT_MAXSEG  2
 #define OPENVPN_TCPOLEN_MAXSEG 4
 
+#pragma pack()
+
 /*
  * The following macro is used to update an
  * internet checksum.  "acc" is a 32-bit
diff --git a/ssl.c b/ssl.c
index 34eb09480d082b101adc8602c2b7d0b0595a7f0b..a8004a6317d3fe60479341e4c09570d29b9ef817 100644 (file)
--- a/ssl.c
+++ b/ssl.c
@@ -860,6 +860,26 @@ tls_lock_common_name (struct tls_multi *multi)
 }
 
 #ifdef ENABLE_DEF_AUTH
+/* key_state_test_auth_control_file return values,
+   NOTE: acf_merge indexing depends on these values */
+#define ACF_UNDEFINED 0
+#define ACF_SUCCEEDED 1
+#define ACF_DISABLED  2
+#define ACF_FAILED    3
+#endif
+
+#ifdef MANAGEMENT_DEF_AUTH
+static inline unsigned int
+man_def_auth_test (const struct key_state *ks)
+{
+  if (management_enable_def_auth (management))
+    return ks->mda_status;
+  else
+    return ACF_DISABLED;
+}
+#endif
+
+#ifdef PLUGIN_DEF_AUTH
 
 /*
  * auth_control_file functions
@@ -890,17 +910,12 @@ key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options
   gc_free (&gc);                                         
 }
 
-/* key_state_test_auth_control_file return values */
-#define ACF_UNDEFINED 0
-#define ACF_SUCCEEDED 1
-#define ACF_DISABLED  2
-#define ACF_FAILED    3
-static int
+static unsigned int
 key_state_test_auth_control_file (struct key_state *ks)
 {
   if (ks && ks->auth_control_file)
     {
-      int ret = ks->auth_control_status;
+      unsigned int ret = ks->auth_control_status;
       if (ret == ACF_UNDEFINED)
        {
          FILE *fp = fopen (ks->auth_control_file, "r");
@@ -935,14 +950,37 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
   bool active = false;
 
 #ifdef ENABLE_DEF_AUTH
-  if (latency && multi->tas_last && multi->tas_last + latency >= now)
-    return TLS_AUTHENTICATION_UNDEFINED;
-  multi->tas_last = now;
+  static const unsigned char acf_merge[] =
+    {
+      ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_UNDEFINED */
+      ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_SUCCEEDED */
+      ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_DISABLED */
+      ACF_FAILED,    /* s1=ACF_UNDEFINED s2=ACF_FAILED */
+      ACF_UNDEFINED, /* s1=ACF_SUCCEEDED s2=ACF_UNDEFINED */
+      ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_SUCCEEDED */
+      ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_DISABLED */
+      ACF_FAILED,    /* s1=ACF_SUCCEEDED s2=ACF_FAILED */
+      ACF_UNDEFINED, /* s1=ACF_DISABLED  s2=ACF_UNDEFINED */
+      ACF_SUCCEEDED, /* s1=ACF_DISABLED  s2=ACF_SUCCEEDED */
+      ACF_DISABLED,  /* s1=ACF_DISABLED  s2=ACF_DISABLED */
+      ACF_FAILED,    /* s1=ACF_DISABLED  s2=ACF_FAILED */
+      ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_UNDEFINED */
+      ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_SUCCEEDED */
+      ACF_FAILED,    /* s1=ACF_FAILED    s2=ACF_DISABLED */
+      ACF_FAILED     /* s1=ACF_FAILED    s2=ACF_FAILED */
+    };
 #endif
 
   if (multi)
     {
       int i;
+
+#ifdef ENABLE_DEF_AUTH
+      if (latency && multi->tas_last && multi->tas_last + latency >= now)
+       return TLS_AUTHENTICATION_UNDEFINED;
+      multi->tas_last = now;
+#endif
+
       for (i = 0; i < KEY_SCAN_SIZE; ++i)
        {
          struct key_state *ks = multi->key_scan[i];
@@ -952,7 +990,16 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
              if (ks->authenticated)
                {
 #ifdef ENABLE_DEF_AUTH
-                 switch (key_state_test_auth_control_file (ks))
+                 unsigned int s1 = ACF_DISABLED;
+                 unsigned int s2 = ACF_DISABLED;
+#ifdef PLUGIN_DEF_AUTH
+                 s1 = key_state_test_auth_control_file (ks); 
+#endif
+#ifdef MANAGEMENT_DEF_AUTH
+                 s2 = man_def_auth_test (ks);
+#endif
+                 ASSERT (s1 < 4 && s2 < 4);
+                 switch (acf_merge[(s1<<2) + s2])
                    {
                    case ACF_SUCCEEDED:
                    case ACF_DISABLED:
@@ -989,6 +1036,28 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
     return TLS_AUTHENTICATION_FAILED;
 }
 
+#ifdef MANAGEMENT_DEF_AUTH
+bool
+tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth)
+{
+  bool ret = false;
+  if (multi)
+    {
+      int i;
+      for (i = 0; i < KEY_SCAN_SIZE; ++i)
+       {
+         struct key_state *ks = multi->key_scan[i];
+         if (ks->mda_key_id == mda_key_id)
+           {
+             ks->mda_status = auth ? ACF_SUCCEEDED : ACF_FAILED;
+             ret = true;
+           }
+       }
+    }
+  return ret;
+}
+#endif
+
 void
 tls_deauthenticate (struct tls_multi *multi)
 {
@@ -1458,7 +1527,7 @@ init_ssl (const struct options *options)
 #if P2MP_SERVER
   if (options->client_cert_not_required)
     {
-      msg (M_WARN, "WARNING: This configuration may accept clients which do not present a certificate");
+      msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION --client-cert-not-required may accept clients which do not present a certificate");
     }
   else
 #endif
@@ -1976,6 +2045,10 @@ key_state_init (struct tls_session *session, struct key_state *ks)
   packet_id_init (&ks->packet_id,
                  session->opt->replay_window,
                  session->opt->replay_time);
+
+#ifdef MANAGEMENT_DEF_AUTH
+  ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++;
+#endif
 }
 
 static void
@@ -2018,7 +2091,7 @@ key_state_free (struct key_state *ks, bool clear)
 
   packet_id_free (&ks->packet_id);
 
-#ifdef ENABLE_DEF_AUTH
+#ifdef PLUGIN_DEF_AUTH
   key_state_rm_auth_control_file (ks);
 #endif
 
@@ -2933,7 +3006,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
       /* setenv client real IP address */
       setenv_untrusted (session);
 
-#ifdef ENABLE_DEF_AUTH
+#ifdef PLUGIN_DEF_AUTH
       /* generate filename for deferred auth control file */
       key_state_gen_auth_control_file (ks, session->opt);
 #endif
@@ -2941,7 +3014,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
       /* call command */
       retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
 
-#ifdef ENABLE_DEF_AUTH
+#ifdef PLUGIN_DEF_AUTH
       /* purge auth control filename (and file itself) for non-deferred returns */
       if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
        key_state_rm_auth_control_file (ks);
@@ -2952,12 +3025,56 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
     }
   else
     {
-      msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
+      msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
     }
 
   return retval;
 }
 
+/*
+ * MANAGEMENT_DEF_AUTH internal ssl.c status codes
+ */
+#define KMDA_ERROR   0
+#define KMDA_SUCCESS 1
+#define KMDA_UNDEF   2
+#define KMDA_DEF     3
+
+#ifdef MANAGEMENT_DEF_AUTH
+static int
+verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
+{
+  int retval = KMDA_ERROR;
+
+  /* Is username defined? */
+  if (strlen (up->username))
+    {
+      /* set username/password in private env space */
+      setenv_str (session->opt->es, "username", raw_username);
+      setenv_str (session->opt->es, "password", up->password);
+
+      /* setenv incoming cert common name for script */
+      setenv_str (session->opt->es, "common_name", session->common_name);
+
+      /* setenv client real IP address */
+      setenv_untrusted (session);
+
+      if (management)
+       management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
+
+      setenv_del (session->opt->es, "password");
+      setenv_str (session->opt->es, "username", up->username);
+
+      retval = KMDA_SUCCESS;
+    }
+  else
+    {
+      msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
+    }
+
+  return retval;
+}
+#endif
+
 /*
  * Handle the reading and writing of key data to and from
  * the TLS control channel (cleartext).
@@ -3134,6 +3251,13 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
   char *options;
   struct user_pass *up;
 
+  bool man_def_auth = KMDA_UNDEF;
+
+#ifdef MANAGEMENT_DEF_AUTH
+  if (management_enable_def_auth (management))
+    man_def_auth = KMDA_DEF;
+#endif
+
   ASSERT (session->opt->key_method == 2);
 
   /* allocate temporary objects */
@@ -3169,7 +3293,8 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
   /* should we check username/password? */
   ks->authenticated = false;
   if (session->opt->auth_user_pass_verify_script
-      || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
+      || plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
+      || man_def_auth == KMDA_DEF)
     {
       int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
       bool s2 = true;
@@ -3195,6 +3320,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
       string_mod (up->password, CC_PRINT, CC_CRLF, '_');
 
       /* call plugin(s) and/or script */
+#ifdef MANAGEMENT_DEF_AUTH
+      if (man_def_auth == KMDA_DEF)
+       man_def_auth = verify_user_pass_management (session, up, raw_username);
+#endif
       if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
        s1 = verify_user_pass_plugin (session, up, raw_username);
       if (session->opt->auth_user_pass_verify_script)
@@ -3202,16 +3331,21 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
       
       /* auth succeeded? */
       if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
-#ifdef ENABLE_DEF_AUTH
+#ifdef PLUGIN_DEF_AUTH
           || s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
 #endif
-          ) && s2)
+          ) && s2 && man_def_auth != KMDA_ERROR)
        {
          ks->authenticated = true;
-#ifdef ENABLE_DEF_AUTH
+#ifdef PLUGIN_DEF_AUTH
          if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
            ks->auth_deferred = true;
 #endif
+#ifdef MANAGEMENT_DEF_AUTH
+         if (man_def_auth != KMDA_UNDEF)
+           ks->auth_deferred = true;
+#endif
+           
          if (session->opt->username_as_common_name)
            set_common_name (session, up->username);
          msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
diff --git a/ssl.h b/ssl.h
index 2f8095fb39ac7b21e2f8bc9c689b7e53cdd89ab7..0b8527d2c344db46a4e6b2291deeb0b0f2bad0f6 100644 (file)
--- a/ssl.h
+++ b/ssl.h
@@ -375,9 +375,15 @@ struct key_state
 #ifdef ENABLE_DEF_AUTH
   /* If auth_deferred is true, authentication is being deferred */
   bool auth_deferred;
+#ifdef MANAGEMENT_DEF_AUTH
+  unsigned int mda_key_id;
+  unsigned int mda_status;
+#endif
+#ifdef PLUGIN_DEF_AUTH
+  unsigned int auth_control_status;
   time_t acf_last_mod;
   char *auth_control_file;
-  int auth_control_status;
+#endif
 #endif
 };
 
@@ -459,6 +465,10 @@ struct tls_options
   struct env_set *es;
   const struct plugin_list *plugins;
 
+#ifdef MANAGEMENT_DEF_AUTH
+  struct man_def_auth_context *mda_context;
+#endif
+
   /* --gremlin bits */
   int gremlin;
 };
@@ -679,6 +689,10 @@ void tls_lock_common_name (struct tls_multi *multi);
 int tls_authentication_status (struct tls_multi *multi, const int latency);
 void tls_deauthenticate (struct tls_multi *multi);
 
+#ifdef MANAGEMENT_DEF_AUTH
+bool tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth);
+#endif
+
 /*
  * inline functions
  */
index bb6a62d61da6499db1be9cb22e06e0f862cf19a7..624ae106f778a51620ff51f09c65dbcf4fa76635 100644 (file)
--- a/syshead.h
+++ b/syshead.h
@@ -471,19 +471,40 @@ socket_defined (const socket_descriptor_t sd)
 #endif
 
 /*
- * Enable deferred authentication
+ * Enable deferred authentication?
  */
-#if defined(ENABLE_PLUGIN) && P2MP_SERVER
+#define CONFIGURE_DEF_AUTH /* this should be set by autoconf and config.h */
+#if defined(CONFIGURE_DEF_AUTH) && defined(P2MP_SERVER) && defined(ENABLE_PLUGIN)
+#define PLUGIN_DEF_AUTH
+#endif
+#if defined(CONFIGURE_DEF_AUTH) && defined(P2MP_SERVER) && defined(ENABLE_MANAGEMENT)
+#define MANAGEMENT_DEF_AUTH
+#endif
+#if defined(PLUGIN_DEF_AUTH) || defined(MANAGEMENT_DEF_AUTH)
 #define ENABLE_DEF_AUTH
 #endif
 
 /*
- * Enable packet filter
+ * Enable packet filter?
  */
-#if defined(ENABLE_PLUGIN) && P2MP_SERVER && defined(HAVE_STAT)
+#define CONFIGURE_PF /* this should be set by autoconf and config.h */
+#if defined(CONFIGURE_PF) && defined(P2MP_SERVER) && defined(ENABLE_PLUGIN) && defined(HAVE_STAT)
+#define PLUGIN_PF
+#endif
+#if defined(CONFIGURE_PF) && defined(P2MP_SERVER) && defined(MANAGEMENT_DEF_AUTH)
+#define MANAGEMENT_PF
+#endif
+#if defined(PLUGIN_PF) || defined(MANAGEMENT_PF)
 #define ENABLE_PF
 #endif
 
+/*
+ * Don't compile the struct buffer_list code unless something needs it
+ */
+#if defined(ENABLE_MANAGEMENT) || defined(ENABLE_PF)
+#define ENABLE_BUFFER_LIST
+#endif
+
 /*
  * Do we have pthread capability?
  */
diff --git a/tun.c b/tun.c
index 3877ca8faf7b42f6c8ee722ca7e4e70ed724f9cb..78d2d6e4d94585d82fb239482fc3ed78272a74cc 100644 (file)
--- a/tun.c
+++ b/tun.c
@@ -1248,7 +1248,7 @@ close_tun (struct tuntap *tt)
 #endif
 
            msg (M_INFO, "%s", command_line);
-           system_check (command_line, NULL, S_FATAL, "Linux ip addr del failed");
+           system_check (command_line, NULL, 0, "Linux ip addr del failed");
 
            gc_free (&gc);
          }
index 381731895541d566d1aae308a9c55ef15943d359..1ce8c483e306908d31761c0fc4bf939ebb1f6cc0 100644 (file)
@@ -1,5 +1,5 @@
 dnl define the OpenVPN version
-define(PRODUCT_VERSION,[2.1_rc7d])
+define(PRODUCT_VERSION,[2.1_rc7e])
 dnl define the TAP version
 define(PRODUCT_TAP_ID,[tap0901])
 define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])