aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--buffer.c127
-rw-r--r--buffer.h34
-rw-r--r--debug/valgrind-suppress566
-rwxr-xr-xdovalns2
-rw-r--r--errlevel.h5
-rw-r--r--error.c5
-rw-r--r--error.h8
-rw-r--r--forward.c2
-rw-r--r--init.c23
-rw-r--r--manage.c536
-rw-r--r--manage.h141
-rw-r--r--management/management-notes.txt262
-rw-r--r--mroute.c213
-rw-r--r--mroute.h73
-rw-r--r--multi.c353
-rw-r--r--multi.h13
-rw-r--r--openvpn.815
-rw-r--r--openvpn.h4
-rw-r--r--options.c61
-rw-r--r--options.h20
-rw-r--r--pf-inline.h59
-rw-r--r--pf.c272
-rw-r--r--pf.h23
-rw-r--r--proto.h22
-rw-r--r--ssl.c174
-rw-r--r--ssl.h16
-rw-r--r--syshead.h29
-rw-r--r--tun.c2
-rw-r--r--version.m42
30 files changed, 2495 insertions, 569 deletions
diff --git a/Makefile.am b/Makefile.am
index 6b94ccd..88a8379 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 \
diff --git a/buffer.c b/buffer.c
index 6ceaacf..92b10e5 100644
--- 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
diff --git a/buffer.h b/buffer.h
index eb37794..195c7d3 100644
--- 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 */
diff --git a/debug/valgrind-suppress b/debug/valgrind-suppress
index 612c45e..a94c61a 100644
--- a/debug/valgrind-suppress
+++ b/debug/valgrind-suppress
@@ -1,118 +1,411 @@
-# 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
}
{
@@ -128,3 +421,172 @@
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
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 $*
diff --git a/errlevel.h b/errlevel.h
index 652bf39..b2a0dda 100644
--- a/errlevel.h
+++ b/errlevel.h
@@ -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 */
@@ -102,6 +102,7 @@
#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 */
@@ -136,6 +137,8 @@
#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 bb5909a..cdc2a11 100644
--- 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 f2eaa12..0f3087b 100644
--- 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)
diff --git a/forward.c b/forward.c
index b7c8b3b..85c1194 100644
--- 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 9d80d1a..97ecf7d 100644
--- 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
diff --git a/manage.c b/manage.c
index 090f691..24cd6b5 100644
--- a/manage.c
+++ b/manage.c
@@ -88,6 +88,15 @@ man_help ()
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.");
msg (M_CLIENT, "state [on|off] [N|all] : Like log, but show state history.");
@@ -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);
}
/*
@@ -2302,108 +2586,6 @@ command_line_next (struct command_line *cl)
}
/*
- * 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
*/
diff --git a/manage.h b/manage.h
index fed14b7..b4d75bf 100644
--- 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
@@ -38,6 +39,22 @@
#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
*/
struct command_line
@@ -55,34 +72,6 @@ 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
*/
diff --git a/management/management-notes.txt b/management/management-notes.txt
index 73f82a5..4d86111 100644
--- a/management/management-notes.txt
+++ b/management/management-notes.txt
@@ -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
---------------
diff --git a/mroute.c b/mroute.c
index 5922b8a..5b383a9 100644
--- 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;
}
@@ -229,6 +286,14 @@ 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)
{
diff --git a/mroute.h b/mroute.h
index 16d2add..4a6f458 100644
--- a/mroute.h
+++ b/mroute.h
@@ -35,10 +35,18 @@
#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 431ad95..bc3500f 100644
--- 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)
@@ -1469,6 +1589,17 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
}
/*
+ * 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 c796c23..3abfeae 100644
--- 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
*/
diff --git a/openvpn.8 b/openvpn.8
index 8a239f9..69d8f8f 100644
--- 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,
diff --git a/openvpn.h b/openvpn.h
index f637cd5..1ffc44c 100644
--- 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
};
/*
diff --git a/options.c b/options.c
index e355e16..5328efb 100644
--- 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;
diff --git a/options.h b/options.h
index 607df0f..39f2ac6 100644
--- 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
index 0000000..617f0bf
--- /dev/null
+++ b/pf-inline.h
@@ -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 87d7c3b..b7ed4c8 100644
--- 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 754db8a..b001bc8 100644
--- 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 77d9c0d..2be24ef 100644
--- 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 34eb094..a8004a6 100644
--- 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,13 +3025,57 @@ 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 2f8095f..0b8527d 100644
--- 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
*/
diff --git a/syshead.h b/syshead.h
index bb6a62d..624ae10 100644
--- a/syshead.h
+++ b/syshead.h
@@ -471,20 +471,41 @@ 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?
*/
#ifdef USE_PTHREAD
diff --git a/tun.c b/tun.c
index 3877ca8..78d2d6e 100644
--- 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);
}
diff --git a/version.m4 b/version.m4
index 3817318..1ce8c48 100644
--- a/version.m4
+++ b/version.m4
@@ -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])