Updated version to 2.1_rc7e.

Added client authentication and packet filtering capability
to management interface.

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

Updated valgrind-suppress file.

Made "Linux ip addr del failed" error nonfatal.

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

Added #pragma pack to proto.h.


git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@2991 e7ae566f-a301-0410-adde-c780ea21d3b5
This commit is contained in:
james 2008-06-11 08:45:09 +00:00
parent 4d84de11b1
commit 90efcacba6
30 changed files with 2552 additions and 626 deletions

View File

@ -112,7 +112,7 @@ openvpn_SOURCES = \
otime.c otime.h \
packet_id.c packet_id.h \
perf.c perf.h \
pf.c pf.h \
pf.c pf.h pf-inline.h \
ping.c ping.h ping-inline.h \
plugin.c plugin.h \
pool.c pool.h \

127
buffer.c
View File

@ -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

View File

@ -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 */

View File

@ -1,118 +1,411 @@
# Valgrind suppressions file for OpenVPN.
#
# Mostly deal with uninitialized data warnings
# in OpenSSL.
{
cond_BN
Memcheck:Cond
fun:BN_*
}
{
value4_BN
Memcheck:Value4
fun:BN_*
}
{
cond_bn
Memcheck:Cond
fun:bn_*
}
{
value4_bn
Memcheck:Value4
fun:bn_*
}
{
cond_SHA1_Update
Memcheck:Cond
fun:SHA1_Update
}
{
value4_SHA1_Update
Memcheck:Value4
fun:SHA1_Update
}
{
cond_ssl3_read_bytes
Memcheck:Cond
fun:ssl3_read_bytes
}
{
cond_crypto
Memcheck:Cond
obj:/lib/libcrypto.so.*
}
{
value4_crypto
Memcheck:Value4
obj:/lib/libcrypto.so.*
}
{
cond_ssl
Memcheck:Cond
obj:/lib/libssl.so.*
}
{
value4_ssl
Memcheck:Value4
obj:/lib/libssl.so.*
}
{
addr4_AES_cbc_encrypt
Memcheck:Addr4
fun:AES_cbc_encrypt
}
{
cond_memcpy_ssl3_read_bytes
Memcheck:Cond
fun:memcpy
fun:ssl3_read_bytes
}
{
value4_memcpy_ssl3_read_bytes
Memcheck:Value4
fun:memcpy
fun:ssl3_read_bytes
}
{
cond_memset_BUF_MEM_grow_clean
Memcheck:Cond
fun:memset
fun:BUF_MEM_grow_clean
}
{
value4_memset_BUF_MEM_grow_clean
Memcheck:Value4
fun:memset
fun:BUF_MEM_grow_clean
<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
}
{
<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
}
{
<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
}
{
<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
}
{
<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
}
{
<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
}
{
<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
}
{
<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:*
obj:*
obj:*
}
{
<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
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
}
{
<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
}
{
<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: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/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
}
{
<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
}
{
<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: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/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
}
{
<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_tcp
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/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: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
}

2
dovalns Executable file
View File

@ -0,0 +1,2 @@
#!/bin/bash
valgrind --tool=memcheck --error-limit=no --gen-suppressions=all --leak-check=full --show-reachable=yes --num-callers=32 $*

View File

@ -94,7 +94,7 @@
#define D_ROUTE_QUOTA LOGLEV(3, 43, 0) /* show route quota exceeded messages */
#define D_OSBUF LOGLEV(3, 44, 0) /* show socket/tun/tap buffer sizes */
#define D_PS_PROXY LOGLEV(3, 45, 0) /* messages related to --port-share option */
#define D_PF LOGLEV(3, 46, 0) /* messages related to packet filter */
#define D_PF_INFO LOGLEV(3, 46, 0) /* packet filter informational messages */
#define D_SHOW_PARMS LOGLEV(4, 50, 0) /* show all parameters on program initiation */
#define D_SHOW_OCC LOGLEV(4, 51, 0) /* show options compatibility string */
@ -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 */

View File

@ -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 = "";

View File

@ -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)

View File

@ -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

23
init.c
View File

@ -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

536
manage.c
View File

@ -87,6 +87,15 @@ man_help ()
#ifdef ENABLE_PKCS11
msg (M_CLIENT, "pkcs11-id-count : Get number of available PKCS#11 identities.");
msg (M_CLIENT, "pkcs11-id-get index : Get PKCS#11 identity at index.");
#endif
#ifdef MANAGEMENT_DEF_AUTH
msg (M_CLIENT, "client-auth CID KID : Authenticate client-id/key-id CID/KID (MULTILINE)");
msg (M_CLIENT, "client-auth-nt CID KID : Authenticate client-id/key-id CID/KID");
msg (M_CLIENT, "client-deny CID KID R : Deny auth client-id/key-id CID/KID with reason text R");
msg (M_CLIENT, "client-kill CID : Kill client instance CID");
#ifdef MANAGEMENT_PF
msg (M_CLIENT, "client-pf CID : Define packet filter for client CID (MULTILINE)");
#endif
#endif
msg (M_CLIENT, "signal s : Send signal s to daemon,");
msg (M_CLIENT, " s = SIGHUP|SIGTERM|SIGUSR1|SIGUSR2.");
@ -178,7 +187,7 @@ man_update_io_state (struct management *man)
{
if (socket_defined (man->connection.sd_cli))
{
if (output_list_defined (man->connection.out))
if (buffer_list_defined (man->connection.out))
{
man->connection.state = MS_CC_WAIT_WRITE;
}
@ -195,7 +204,7 @@ man_output_list_push (struct management *man, const char *str)
if (management_connected (man))
{
if (str)
output_list_push (man->connection.out, (const unsigned char *) str);
buffer_list_push (man->connection.out, (const unsigned char *) str);
man_update_io_state (man);
if (!man->persist.standalone_disabled)
{
@ -672,12 +681,12 @@ man_hold (struct management *man, const char *cmd)
{
if (streq (cmd, "on"))
{
man->settings.hold = true;
man->settings.flags |= MF_HOLD;
msg (M_CLIENT, "SUCCESS: hold flag set to ON");
}
else if (streq (cmd, "off"))
{
man->settings.hold = false;
man->settings.flags &= ~MF_HOLD;
msg (M_CLIENT, "SUCCESS: hold flag set to OFF");
}
else if (streq (cmd, "release"))
@ -691,9 +700,205 @@ man_hold (struct management *man, const char *cmd)
}
}
else
msg (M_CLIENT, "SUCCESS: hold=%d", (int) man->settings.hold);
msg (M_CLIENT, "SUCCESS: hold=%d", BOOL_CAST(man->settings.flags & MF_HOLD));
}
#ifdef MANAGEMENT_DEF_AUTH
static bool
parse_cid (const char *str, unsigned long *cid)
{
if (sscanf (str, "%lu", cid) == 1)
return true;
else
{
msg (M_CLIENT, "ERROR: cannot parse CID");
return false;
}
}
static bool
parse_kid (const char *str, unsigned int *kid)
{
if (sscanf (str, "%u", kid) == 1)
return true;
else
{
msg (M_CLIENT, "ERROR: cannot parse KID");
return false;
}
}
static void
in_extra_reset (struct man_connection *mc, const bool new)
{
if (mc)
{
if (!new)
{
mc->in_extra_cmd = IEC_UNDEF;
mc->in_extra_cid = 0;
mc->in_extra_kid = 0;
}
if (mc->in_extra)
{
buffer_list_free (mc->in_extra);
mc->in_extra = NULL;
}
if (new)
mc->in_extra = buffer_list_new (0);
}
}
static void
in_extra_dispatch (struct management *man)
{
switch (man->connection.in_extra_cmd)
{
case IEC_CLIENT_AUTH:
if (man->persist.callback.client_auth)
{
const bool status = (*man->persist.callback.client_auth)
(man->persist.callback.arg,
man->connection.in_extra_cid,
man->connection.in_extra_kid,
true,
NULL,
man->connection.in_extra);
man->connection.in_extra = NULL;
if (status)
{
msg (M_CLIENT, "SUCCESS: client-auth command succeeded");
}
else
{
msg (M_CLIENT, "ERROR: client-auth command failed");
}
}
else
{
msg (M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode");
}
break;
#ifdef MANAGEMENT_PF
case IEC_CLIENT_PF:
if (man->persist.callback.client_pf)
{
const bool status = (*man->persist.callback.client_pf)
(man->persist.callback.arg,
man->connection.in_extra_cid,
man->connection.in_extra);
man->connection.in_extra = NULL;
if (status)
{
msg (M_CLIENT, "SUCCESS: client-pf command succeeded");
}
else
{
msg (M_CLIENT, "ERROR: client-pf command failed");
}
}
else
{
msg (M_CLIENT, "ERROR: The client-pf command is not supported by the current daemon mode");
}
break;
#endif
}
in_extra_reset (&man->connection, false);
}
static void
man_client_auth (struct management *man, const char *cid_str, const char *kid_str, const bool extra)
{
struct man_connection *mc = &man->connection;
mc->in_extra_cid = 0;
mc->in_extra_kid = 0;
if (parse_cid (cid_str, &mc->in_extra_cid)
&& parse_kid (kid_str, &mc->in_extra_kid))
{
mc->in_extra_cmd = IEC_CLIENT_AUTH;
in_extra_reset (mc, true);
if (!extra)
in_extra_dispatch (man);
}
}
static void
man_client_deny (struct management *man, const char *cid_str, const char *kid_str, const char *reason)
{
unsigned long cid = 0;
unsigned int kid = 0;
if (parse_cid (cid_str, &cid) && parse_kid (kid_str, &kid))
{
if (man->persist.callback.client_auth)
{
const bool status = (*man->persist.callback.client_auth)
(man->persist.callback.arg,
cid,
kid,
false,
reason,
NULL);
if (status)
{
msg (M_CLIENT, "SUCCESS: client-deny command succeeded");
}
else
{
msg (M_CLIENT, "ERROR: client-deny command failed");
}
}
else
{
msg (M_CLIENT, "ERROR: The client-deny command is not supported by the current daemon mode");
}
}
}
static void
man_client_kill (struct management *man, const char *cid_str)
{
unsigned long cid = 0;
if (parse_cid (cid_str, &cid))
{
if (man->persist.callback.kill_by_cid)
{
const bool status = (*man->persist.callback.kill_by_cid) (man->persist.callback.arg, cid);
if (status)
{
msg (M_CLIENT, "SUCCESS: client-kill command succeeded");
}
else
{
msg (M_CLIENT, "ERROR: client-kill command failed");
}
}
else
{
msg (M_CLIENT, "ERROR: The client-kill command is not supported by the current daemon mode");
}
}
}
#ifdef MANAGEMENT_PF
static void
man_client_pf (struct management *man, const char *cid_str)
{
struct man_connection *mc = &man->connection;
mc->in_extra_cid = 0;
mc->in_extra_kid = 0;
if (parse_cid (cid_str, &mc->in_extra_cid))
{
mc->in_extra_cmd = IEC_CLIENT_PF;
in_extra_reset (mc, true);
}
}
#endif
#endif
#define MN_AT_LEAST (1<<0)
static bool
@ -867,6 +1072,35 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch
if (man_need (man, p, 1, 0))
man_bytecount (man, atoi(p[1]));
}
#ifdef MANAGEMENT_DEF_AUTH
else if (streq (p[0], "client-kill"))
{
if (man_need (man, p, 1, 0))
man_client_kill (man, p[1]);
}
else if (streq (p[0], "client-deny"))
{
if (man_need (man, p, 3, 0))
man_client_deny (man, p[1], p[2], p[3]);
}
else if (streq (p[0], "client-auth-nt"))
{
if (man_need (man, p, 2, 0))
man_client_auth (man, p[1], p[2], false);
}
else if (streq (p[0], "client-auth"))
{
if (man_need (man, p, 2, 0))
man_client_auth (man, p[1], p[2], true);
}
#ifdef MANAGEMENT_PF
else if (streq (p[0], "client-pf"))
{
if (man_need (man, p, 1, 0))
man_client_pf (man, p[1]);
}
#endif
#endif
#ifdef ENABLE_PKCS11
else if (streq (p[0], "pkcs11-id-count"))
{
@ -999,7 +1233,7 @@ man_new_connection_post (struct management *man, const char *description)
description,
print_sockaddr (&man->settings.local, &gc));
output_list_reset (man->connection.out);
buffer_list_reset (man->connection.out);
if (!man_password_needed (man))
man_welcome (man);
@ -1134,14 +1368,17 @@ man_reset_client_socket (struct management *man, const bool exiting)
man_close_socket (man, man->connection.sd_cli);
man->connection.sd_cli = SOCKET_UNDEFINED;
command_line_reset (man->connection.in);
output_list_reset (man->connection.out);
buffer_list_reset (man->connection.out);
#ifdef MANAGEMENT_DEF_AUTH
in_extra_reset (&man->connection, false);
#endif
}
if (!exiting)
{
if (man->settings.management_forget_disconnect)
if (man->settings.flags & MF_FORGET_DISCONNECT)
ssl_purge_auth ();
if (man->settings.signal_on_disconnect) {
if (man->settings.flags & MF_SIGNAL) {
int mysig = man_mod_signal (man, SIGUSR1);
if (mysig >= 0)
{
@ -1150,7 +1387,7 @@ man_reset_client_socket (struct management *man, const bool exiting)
}
}
if (man->settings.connect_as_client)
if (man->settings.flags & MF_CONNECT_AS_CLIENT)
{
msg (D_MANAGEMENT, "MANAGEMENT: Triggering management exit");
throw_signal_soft (SIGTERM, "management-exit");
@ -1170,6 +1407,9 @@ man_process_command (struct management *man, const char *line)
CLEAR (parms);
so = status_open (NULL, 0, -1, &man->persist.vout, 0);
#ifdef MANAGEMENT_DEF_AUTH
in_extra_reset (&man->connection, false);
#endif
if (man_password_needed (man))
{
@ -1243,7 +1483,7 @@ man_read (struct management *man)
/*
* Reset output object
*/
output_list_reset (man->connection.out);
buffer_list_reset (man->connection.out);
/*
* process command line if complete
@ -1252,7 +1492,22 @@ man_read (struct management *man)
const unsigned char *line;
while ((line = command_line_get (man->connection.in)))
{
man_process_command (man, (char *) line);
#ifdef MANAGEMENT_DEF_AUTH
if (man->connection.in_extra)
{
if (!strcmp ((char *)line, "END"))
{
in_extra_dispatch (man);
in_extra_reset (&man->connection, false);
}
else
{
buffer_list_push (man->connection.in_extra, line);
}
}
else
#endif
man_process_command (man, (char *) line);
if (man->connection.halt)
break;
command_line_next (man->connection.in);
@ -1289,14 +1544,14 @@ man_write (struct management *man)
const int max_send = 256;
int sent = 0;
const struct buffer *buf = output_list_peek (man->connection.out);
const struct buffer *buf = buffer_list_peek (man->connection.out);
if (buf && BLEN (buf))
{
const int len = min_int (max_send, BLEN (buf));
sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL);
if (sent >= 0)
{
output_list_advance (man->connection.out, sent);
buffer_list_advance (man->connection.out, sent);
}
else if (sent < 0)
{
@ -1387,27 +1642,18 @@ man_settings_init (struct man_settings *ms,
const char *addr,
const int port,
const char *pass_file,
const bool server,
const bool query_passwords,
const int log_history_cache,
const int echo_buffer_size,
const int state_buffer_size,
const bool hold,
const bool signal_on_disconnect,
const bool management_forget_disconnect,
const bool connect_as_client,
const char *write_peer_info_file,
const int remap_sigusr1)
const int remap_sigusr1,
const unsigned int flags)
{
if (!ms->defined)
{
CLEAR (*ms);
/*
* Are we a server? If so, it will influence
* the way we handle state transitions.
*/
ms->server = server;
ms->flags = flags;
/*
* Get username/password
@ -1415,34 +1661,6 @@ man_settings_init (struct man_settings *ms,
if (pass_file)
get_user_pass (&ms->up, pass_file, "Management", GET_USER_PASS_PASSWORD_ONLY);
/*
* Should OpenVPN query the management layer for
* passwords?
*/
ms->up_query_passwords = query_passwords;
/*
* Should OpenVPN hibernate on startup?
*/
ms->hold = hold;
/*
* Should OpenVPN be signaled if management
* disconnects?
*/
ms->signal_on_disconnect = signal_on_disconnect;
/*
* Should OpenVPN forget passwords when managmenet
* session disconnects?
*/
ms->management_forget_disconnect = management_forget_disconnect;
/*
* Should OpenVPN connect to management interface as a client
* rather than a server?
*/
ms->connect_as_client = connect_as_client;
ms->write_peer_info_file = string_alloc (write_peer_info_file, NULL);
/*
@ -1456,7 +1674,7 @@ man_settings_init (struct man_settings *ms,
* Run management over tunnel, or
* separate channel?
*/
if (streq (addr, "tunnel") && !connect_as_client)
if (streq (addr, "tunnel") && !(flags & MF_CONNECT_AS_CLIENT))
{
ms->management_over_tunnel = true;
}
@ -1511,7 +1729,7 @@ man_connection_init (struct management *man)
* command output from/to the socket.
*/
man->connection.in = command_line_new (256);
man->connection.out = output_list_new (0);
man->connection.out = buffer_list_new (0);
/*
* Initialize event set for standalone usage, when we are
@ -1525,7 +1743,7 @@ man_connection_init (struct management *man)
/*
* Listen/connect socket
*/
if (man->settings.connect_as_client)
if (man->settings.flags & MF_CONNECT_AS_CLIENT)
man_connect (man);
else
man_listen (man);
@ -1549,7 +1767,10 @@ man_connection_close (struct management *man)
if (mc->in)
command_line_free (mc->in);
if (mc->out)
output_list_free (mc->out);
buffer_list_free (mc->out);
#ifdef MANAGEMENT_DEF_AUTH
in_extra_reset (&man->connection, false);
#endif
man_connection_clear (mc);
}
@ -1574,17 +1795,12 @@ management_open (struct management *man,
const char *addr,
const int port,
const char *pass_file,
const bool server,
const bool query_passwords,
const int log_history_cache,
const int echo_buffer_size,
const int state_buffer_size,
const bool hold,
const bool signal_on_disconnect,
const bool management_forget_disconnect,
const bool connect_as_client,
const char *write_peer_info_file,
const int remap_sigusr1)
const int remap_sigusr1,
const unsigned int flags)
{
bool ret = false;
@ -1596,17 +1812,12 @@ management_open (struct management *man,
addr,
port,
pass_file,
server,
query_passwords,
log_history_cache,
echo_buffer_size,
state_buffer_size,
hold,
signal_on_disconnect,
management_forget_disconnect,
connect_as_client,
write_peer_info_file,
remap_sigusr1);
remap_sigusr1,
flags);
/*
* The log is initially sized to MANAGEMENT_LOG_HISTORY_INITIAL_SIZE,
@ -1665,7 +1876,7 @@ management_set_state (struct management *man,
const in_addr_t tun_local_ip,
const in_addr_t tun_remote_ip)
{
if (man->persist.state && (!man->settings.server || state < OPENVPN_STATE_CLIENT_BASE))
if (man->persist.state && (!(man->settings.flags & MF_SERVER) || state < OPENVPN_STATE_CLIENT_BASE))
{
struct gc_arena gc = gc_new ();
struct log_entry e;
@ -1697,6 +1908,79 @@ management_set_state (struct management *man,
}
}
#ifdef MANAGEMENT_DEF_AUTH
static void
man_output_env (const struct env_set *es)
{
if (es)
{
struct env_item *e;
for (e = es->list; e != NULL; e = e->next)
{
if (e->string)
msg (M_CLIENT, ">CLIENT:ENV,%s", e->string);
}
}
msg (M_CLIENT, ">CLIENT:ENV,END");
}
void
management_notify_client_needing_auth (struct management *management,
const unsigned int mda_key_id,
struct man_def_auth_context *mdac,
const struct env_set *es)
{
if (!(mdac->flags & DAF_CONNECTION_CLOSED))
{
const char *mode = "CONNECT";
if (mdac->flags & DAF_CONNECTION_ESTABLISHED)
mode = "REAUTH";
msg (M_CLIENT, ">CLIENT:%s,%lu,%u", mode, mdac->cid, mda_key_id);
man_output_env (es);
mdac->flags |= DAF_INITIAL_AUTH;
}
}
void
management_connection_established (struct management *management,
struct man_def_auth_context *mdac)
{
mdac->flags |= DAF_CONNECTION_ESTABLISHED;
}
void
management_notify_client_close (struct management *management,
struct man_def_auth_context *mdac,
const struct env_set *es)
{
if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
{
msg (M_CLIENT, ">CLIENT:DISCONNECT,%lu", mdac->cid);
man_output_env (es);
mdac->flags |= DAF_CONNECTION_CLOSED;
}
}
void
management_learn_addr (struct management *management,
struct man_def_auth_context *mdac,
const struct mroute_addr *addr,
const bool primary)
{
struct gc_arena gc = gc_new ();
if ((mdac->flags & DAF_INITIAL_AUTH) && !(mdac->flags & DAF_CONNECTION_CLOSED))
{
msg (M_CLIENT, ">CLIENT:ADDRESS,%lu,%s,%d",
mdac->cid,
mroute_addr_print_ex (addr, MAPF_SUBNET, &gc),
BOOL_CAST (primary));
}
gc_free (&gc);
}
#endif
void
management_echo (struct management *man, const char *string, const bool pull)
{
@ -2178,7 +2462,7 @@ management_query_user_pass (struct management *man,
bool
management_would_hold (struct management *man)
{
return man->settings.hold && !man->persist.hold_release && man_standalone_ok (man);
return (man->settings.flags & MF_HOLD) && !man->persist.hold_release && man_standalone_ok (man);
}
/*
@ -2188,7 +2472,7 @@ management_would_hold (struct management *man)
bool
management_should_daemonize (struct management *man)
{
return management_would_hold (man) || man->settings.up_query_passwords;
return management_would_hold (man) || (man->settings.flags & MF_QUERY_PASSWORDS);
}
/*
@ -2301,108 +2585,6 @@ command_line_next (struct command_line *cl)
buf_clear (&cl->residual);
}
/*
* struct output_list
*/
struct output_list *
output_list_new (const int max_size)
{
struct output_list *ret;
ALLOC_OBJ_CLEAR (ret, struct output_list);
ret->max_size = max_size;
ret->size = 0;
return ret;
}
void
output_list_free (struct output_list *ol)
{
output_list_reset (ol);
free (ol);
}
bool
output_list_defined (const struct output_list *ol)
{
return ol->head != NULL;
}
void
output_list_reset (struct output_list *ol)
{
struct output_entry *e = ol->head;
while (e)
{
struct output_entry *next = e->next;
free_buf (&e->buf);
free (e);
e = next;
}
ol->head = ol->tail = NULL;
ol->size = 0;
}
void
output_list_push (struct output_list *ol, const unsigned char *str)
{
if (!ol->max_size || ol->size < ol->max_size)
{
struct output_entry *e;
ALLOC_OBJ_CLEAR (e, struct output_entry);
++ol->size;
if (ol->tail)
{
ASSERT (ol->head);
ol->tail->next = e;
}
else
{
ASSERT (!ol->head);
ol->head = e;
}
e->buf = string_alloc_buf ((const char *) str, NULL);
ol->tail = e;
}
}
const struct buffer *
output_list_peek (struct output_list *ol)
{
if (ol->head)
return &ol->head->buf;
else
return NULL;
}
static void
output_list_pop (struct output_list *ol)
{
if (ol->head)
{
struct output_entry *e = ol->head->next;
free_buf (&ol->head->buf);
free (ol->head);
ol->head = e;
--ol->size;
if (!e)
ol->tail = NULL;
}
}
void
output_list_advance (struct output_list *ol, int n)
{
if (ol->head)
{
struct buffer *buf = &ol->head->buf;
ASSERT (buf_advance (buf, n));
if (!BLEN (buf))
output_list_pop (ol);
}
}
/*
* struct log_entry
*/

139
manage.h
View File

@ -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
@ -37,6 +38,22 @@
#define MANAGEMENT_ECHO_BUFFER_SIZE 100
#define MANAGEMENT_STATE_BUFFER_SIZE 100
/*
* Management-interface-based deferred authentication
*/
#ifdef MANAGEMENT_DEF_AUTH
struct man_def_auth_context {
unsigned long cid;
#define DAF_CONNECTION_ESTABLISHED (1<<0)
#define DAF_CONNECTION_CLOSED (1<<1)
#define DAF_INITIAL_AUTH (1<<2)
unsigned int flags;
unsigned int mda_key_id_counter;
};
#endif
/*
* Manage build-up of command line
*/
@ -54,34 +71,6 @@ const unsigned char *command_line_get (struct command_line *cl);
void command_line_reset (struct command_line *cl);
void command_line_next (struct command_line *cl);
/*
* Manage lists of output strings
*/
struct output_entry
{
struct buffer buf;
struct output_entry *next;
};
struct output_list
{
struct output_entry *head; /* next item to pop/peek */
struct output_entry *tail; /* last item pushed */
int size; /* current number of entries */
int max_size; /* maximum size list should grow to */
};
struct output_list *output_list_new (const int max_size);
void output_list_free (struct output_list *ol);
bool output_list_defined (const struct output_list *ol);
void output_list_reset (struct output_list *ol);
void output_list_push (struct output_list *ol, const unsigned char *str);
const struct buffer *output_list_peek (struct output_list *ol);
void output_list_advance (struct output_list *ol, int n);
/*
* Manage log file history
*/
@ -148,7 +137,8 @@ log_history_capacity (const struct log_history *h)
}
/*
* Callbacks for 'status' and 'kill' commands
* Callbacks for 'status' and 'kill' commands.
* Also for management-based deferred authentication and packet filter.
*/
struct management_callback
{
@ -158,6 +148,20 @@ struct management_callback
int (*kill_by_cn) (void *arg, const char *common_name);
int (*kill_by_addr) (void *arg, const in_addr_t addr, const int port);
void (*delete_event) (void *arg, event_t event);
#ifdef MANAGEMENT_DEF_AUTH
bool (*kill_by_cid) (void *arg, const unsigned long cid);
bool (*client_auth) (void *arg,
const unsigned long cid,
const unsigned int mda_key_id,
const bool auth,
const char *reason,
struct buffer_list *cc_config); /* ownership transferred */
#endif
#ifdef MANAGEMENT_PF
bool (*client_pf) (void *arg,
const unsigned long cid,
struct buffer_list *pf_config); /* ownership transferred */
#endif
};
/*
@ -196,18 +200,13 @@ struct man_persist {
struct man_settings {
bool defined;
unsigned int flags; /* MF_x flags */
struct openvpn_sockaddr local;
bool up_query_passwords;
bool management_over_tunnel;
struct user_pass up;
int log_history_cache;
int echo_buffer_size;
int state_buffer_size;
bool server;
bool hold;
bool signal_on_disconnect;
bool management_forget_disconnect;
bool connect_as_client;
char *write_peer_info_file;
/* flags for handling the management interface "signal" command */
@ -246,8 +245,17 @@ struct man_connection {
int password_tries;
struct command_line *in;
struct output_list *out;
struct buffer_list *out;
#ifdef MANAGEMENT_DEF_AUTH
# define IEC_UNDEF 0
# define IEC_CLIENT_AUTH 1
# define IEC_CLIENT_PF 2
int in_extra_cmd;
unsigned long in_extra_cid;
unsigned int in_extra_kid;
struct buffer_list *in_extra;
#endif
struct event_set *es;
bool state_realtime;
@ -274,21 +282,29 @@ struct user_pass;
struct management *management_init (void);
/* management_open flags */
# define MF_SERVER (1<<0)
# define MF_QUERY_PASSWORDS (1<<1)
# define MF_HOLD (1<<2)
# define MF_SIGNAL (1<<3)
# define MF_FORGET_DISCONNECT (1<<4)
# define MF_CONNECT_AS_CLIENT (1<<5)
#ifdef MANAGEMENT_DEF_AUTH
# define MF_CLIENT_AUTH (1<<6)
#endif
#ifdef MANAGEMENT_PF
# define MF_CLIENT_PF (1<<7)
#endif
bool management_open (struct management *man,
const char *addr,
const int port,
const char *pass_file,
const bool server,
const bool query_passwords,
const int log_history_cache,
const int echo_buffer_size,
const int state_buffer_size,
const bool hold,
const bool signal_on_disconnect,
const bool management_forget_disconnect,
const bool connect_as_client,
const char *write_peer_info_file,
const int remap_sigusr1);
const int remap_sigusr1,
const unsigned int flags);
void management_close (struct management *man);
@ -316,6 +332,25 @@ bool management_hold (struct management *man);
void management_event_loop_n_seconds (struct management *man, int sec);
#ifdef MANAGEMENT_DEF_AUTH
void management_notify_client_needing_auth (struct management *management,
const unsigned int auth_id,
struct man_def_auth_context *mdac,
const struct env_set *es);
void management_connection_established (struct management *management,
struct man_def_auth_context *mdac);
void management_notify_client_close (struct management *management,
struct man_def_auth_context *mdac,
const struct env_set *es);
void management_learn_addr (struct management *management,
struct man_def_auth_context *mdac,
const struct mroute_addr *addr,
const bool primary);
#endif
static inline bool
management_connected (const struct management *man)
{
@ -325,9 +360,25 @@ management_connected (const struct management *man)
static inline bool
management_query_user_pass_enabled (const struct management *man)
{
return man->settings.up_query_passwords;
return BOOL_CAST(man->settings.flags & MF_QUERY_PASSWORDS);
}
#ifdef MANAGEMENT_PF
static inline bool
management_enable_pf (const struct management *man)
{
return man && BOOL_CAST(man->settings.flags & MF_CLIENT_PF);
}
#endif
#ifdef MANAGEMENT_DEF_AUTH
static inline bool
management_enable_def_auth (const struct management *man)
{
return man && BOOL_CAST(man->settings.flags & MF_CLIENT_AUTH);
}
#endif
/*
* OpenVPN tells the management layer what state it's in
*/

View File

@ -25,13 +25,12 @@ Future versions of the management interface may allow out-of-band
connections (i.e. not over the VPN) and secured with SSL/TLS.
The management interface is enabled in the OpenVPN
configuration file using the following directives:
configuration file using the following directive:
--management
--management-query-passwords
--management-log-cache
See the man page for documentation on these directives.
See the man page for documentation on this and related
directives.
Once OpenVPN has started with the management layer enabled,
you can telnet to the management port (make sure to use
@ -444,6 +443,199 @@ Example:
pkcs11-id-get 1
PKCS11ID-ENTRY:'1', ID:'<snip>', BLOB:'<snip>'
COMMAND -- client-auth (OpenVPN 2.1 or higher)
-----------------------------------------------
Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request and specify
"client-connect" configuration directives in a subsequent text block.
The OpenVPN server should have been started with the
--management-client-auth directive so that it will ask the management
interface to approve client connections.
client-auth {CID} {KID}
line_1
line_2
...
line_n
END
CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
notification for more info.
line_1 to line_n -- client-connect configuration text block, as would be
returned by a --client-connect script. The text block may be null, with
"END" immediately following the "client-auth" line (using a null text
block is equivalent to using the client-auth-nt command).
A client-connect configuration text block contains OpenVPN directives
that will be applied to the client instance object representing a newly
connected client.
COMMAND -- client-auth-nt (OpenVPN 2.1 or higher)
--------------------------------------------------
Authorize a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request without specifying
client-connect configuration text.
The OpenVPN server should have been started with the
--management-client-auth directive so that it will ask the management
interface to approve client connections.
client-auth-nt {CID} {KID}
CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
notification for more info.
COMMAND -- client-deny (OpenVPN 2.1 or higher)
-----------------------------------------------
Deny a ">CLIENT:CONNECT" or ">CLIENT:REAUTH" request.
client-deny {CID} {KID} "reason-text"
CID,KID -- client ID and Key ID. See documentation for ">CLIENT:"
notification for more info.
reason-text: a human-readable message explaining why the authentication
request was denied. This message will be output to the OpenVPN log
file or syslog.
Note that client-deny denies a specific Key ID (pertaining to a
TLS renegotiation). A client-deny command issued in response to
an initial TLS key negotiation (notified by ">CLIENT:CONNECT") will
terminate the client session after returning "AUTH-FAILED" to the client.
On the other hand, a client-deny command issued in response to
a TLS renegotiation (">CLIENT:REAUTH") will invalidate the renegotiated
key, however the TLS session associated with the currently active
key will continue to live for up to --tran-window seconds before
expiration.
To immediately kill a client session, use "client-kill".
COMMAND -- client-kill (OpenVPN 2.1 or higher)
-----------------------------------------------
Immediately kill a client instance by CID.
client-kill {CID}
CID -- client ID. See documentation for ">CLIENT:" notification for more
info.
COMMAND -- client-pf (OpenVPN 2.1 or higher)
---------------------------------------------
Push a packet filter file to a specific client.
The OpenVPN server should have been started with the
--management-client-pf directive so that it will require that
VPN tunnel packets sent or received by client instances must
conform to that client's packet filter configuration.
client-pf {CID}
line_1
line_2
...
line_n
END
CID -- client ID. See documentation for ">CLIENT:" notification for
more info.
line_1 to line_n -- the packet filter configuration file for this
client.
Packet filter file grammar:
[CLIENTS DROP|ACCEPT]
{+|-}common_name1
{+|-}common_name2
. . .
[SUBNETS DROP|ACCEPT]
{+|-}subnet1
{+|-}subnet2
. . .
[END]
Subnet: IP-ADDRESS | IP-ADDRESS/NUM_NETWORK_BITS | "unknown"
CLIENTS refers to the set of clients (by their common-name) which
this instance is allowed ('+') to connect to, or is excluded ('-')
from connecting to. Note that in the case of client-to-client
connections, such communication must be allowed by the packet filter
configuration files of both clients AND the --client-to-client
directive must have been specified in the OpenVPN server config.
SUBNETS refers to IP addresses or IP address subnets which this
client instance may connect to ('+') or is excluded ('-') from
connecting to, and applies to IPv4 and ARP packets. The special
"unknown" tag refers to packets of unknown type, i.e. a packet that
is not IPv4 or ARP.
DROP or ACCEPT defines default policy when there is no explicit match
for a common-name or subnet. The [END] tag must exist.
Notes:
* The SUBNETS section currently only supports IPv4 addresses and
subnets.
* A given client or subnet rule applies to both incoming and
outgoing packets.
* The CLIENTS list is order-invariant. Because the list is stored
as a hash-table, the order of the list does not affect its function.
* The SUBNETS table is scanned sequentially, and the first item to
match is chosen. Therefore the SUBNETS table is NOT order-invariant.
* No client-to-client communication is allowed unless the
--client-to-client configuration directive is enabled AND
the CLIENTS list of BOTH clients allows the communication.
Example packet filter spec, as transmitted to the management interface:
client-pf 42
[CLIENTS ACCEPT]
-accounting
-enigma
[SUBNETS DROP]
-10.46.79.9
+10.0.0.0/8
[END]
END
The above example sets the packet filter policy for the client
identified by CID=42. This client may connect to all other clients
except those having a common name of "accounting" or "enigma".
The client may only interact with external IP addresses in the
10.0.0.0/8 subnet, however access to 10.46.79.9 is specifically
excluded.
Another example packet filter spec, as transmitted to the
management interface:
client-pf 99
[CLIENTS DENY]
+public
[SUBNETS ACCEPT]
+10.10.0.1
-10.0.0.0/8
-unknown
[END]
END
The above example sets the packet filter policy for the client
identified by CID=99. This client may not connect to any other
clients except those having a common name of "public". It may
interact with any external IP address except those in the
10.0.0.0/8 netblock. However interaction with one address in
the 10.0.0.0/8 netblock is allowed: 10.10.0.1. Also, the client
may not interact with external IP addresses using an "unknown"
protocol (i.e. one that is not IPv4 or ARP).
OUTPUT FORMAT
-------------
@ -454,7 +646,7 @@ OUTPUT FORMAT
the last line will be "END".
(3) Real-time messages will be in the form ">[source]:[text]",
where source is "ECHO", "FATAL", "HOLD", "INFO", "LOG",
where source is "CLIENT", "ECHO", "FATAL", "HOLD", "INFO", "LOG",
"NEED-OK", "PASSWORD", or "STATE".
REAL-TIME MESSAGE FORMAT
@ -469,6 +661,12 @@ column and are immediately followed by a type keyword
indicating the type of real-time message. The following
types are currently defined:
CLIENT -- Notification of client connections and disconnections
on an OpenVPN server. Enabled when OpenVPN is started
with the --management-client-auth option. CLIENT
notifications may be multi-line. See "The CLIENT
notification" section below for detailed info.
ECHO -- Echo messages as controlled by the "echo" command.
FATAL -- A fatal error which is output to the log file just
@ -497,6 +695,60 @@ PASSWORD -- Used to tell the management client that OpenVPN
STATE -- Shows the current OpenVPN state, as controlled
by the "state" command.
The CLIENT notification
-----------------------
The ">CLIENT:" notification is enabled by the --management-client-auth
OpenVPN configuration directive that gives the management interface client
the responsibility to authenticate OpenVPN clients after their client
certificate has been verified. CLIENT notifications may be multi-line, and
the sequentiality of a given CLIENT notification, its associated environmental
variables, and the terminating ">CLIENT:ENV,END" line are guaranteed to be
atomic.
CLIENT notification types:
(1) Notify new client connection ("CONNECT") or existing client TLS session
renegotiation ("REAUTH"). Information about the client is provided
by a list of environmental variables which are documented in the OpenVPN
man page. The environmental variables passed are equivalent to those
that would be passed to an --auth-user-pass-verify script.
>CLIENT:CONNECT|REAUTH,{CID},{KID}
>CLIENT:ENV,name1=val1
>CLIENT:ENV,name2=val2
>CLIENT:ENV,...
>CLIENT:ENV,END
(2) Notify existing client disconnection. The environmental variables passed
are equivalent to those that would be passed to a --client-disconnect
script.
>CLIENT:DISCONNECT,{CID}
>CLIENT:ENV,name1=val1
>CLIENT:ENV,name2=val2
>CLIENT:ENV,...
>CLIENT:ENV,END
(3) Notify that a particular virtual address or subnet
is now associated with a specific client.
>CLIENT:ADDRESS,{CID},{ADDR},{PRI}
Variables:
CID -- Client ID, numerical ID for each connecting client, sequence = 0,1,2,...
KID -- Key ID, numerical ID for the key associated with a given client TLS session,
sequence = 0,1,2,...
PRI -- Primary (1) or Secondary (0) VPN address/subnet. All clients have at least
one primary IP address. Secondary address/subnets are associated with
client-specific "iroute" directives.
ADDR -- IPv4 address/subnet in the form 1.2.3.4 or 1.2.3.0/255.255.255.0
In the unlikely scenario of an extremely long-running OpenVPN server,
CID and KID should be assumed to recycle to 0 after (2^32)-1, however this
recycling behavior is guaranteed to be collision-free.
Command Parsing
---------------

225
mroute.c
View File

@ -76,91 +76,148 @@ 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.
*/
unsigned int
mroute_extract_addr_from_packet (struct mroute_addr *src,
struct mroute_addr *dest,
struct buffer *buf,
int tunnel_type)
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;
verify_align_4 (buf);
if (tunnel_type == DEV_TYPE_TUN)
if (BLEN (buf) >= (int) sizeof (struct openvpn_arp))
{
if (BLEN (buf) >= 1)
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)
{
switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
{
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);
mroute_get_in_addr_t (src, arp->ip_src, MR_ARP);
mroute_get_in_addr_t (dest, arp->ip_dest, MR_ARP);
/* mcast address? */
if ((ip->daddr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK))
ret |= MROUTE_EXTRACT_MCAST;
/* multicast packet? */
if (mroute_is_mcast (arp->ip_dest))
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)
{
if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
{
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)
{
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;
}
}
return ret;
}
#endif
unsigned int
mroute_extract_addr_ipv4 (struct mroute_addr *src,
struct mroute_addr *dest,
const struct buffer *buf)
{
unsigned int ret = 0;
if (BLEN (buf) >= 1)
{
switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
{
case 4:
if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
{
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;
}
}
}
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))
{
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)
{
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)))
{
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;
}
}
}
#endif
}
return ret;
}
/*
* Translate a struct openvpn_sockaddr (osaddr)
* to a struct mroute_addr (addr).
@ -228,6 +285,14 @@ mroute_addr_compare_function (const void *key1, const void *key2)
const char *
mroute_addr_print (const struct mroute_addr *ma,
struct gc_arena *gc)
{
return mroute_addr_print_ex (ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
}
const char *
mroute_addr_print_ex (const struct mroute_addr *ma,
const unsigned int flags,
struct gc_arena *gc)
{
struct buffer out = alloc_buf_gc (64, gc);
if (ma)
@ -249,9 +314,19 @@ mroute_addr_print (const struct mroute_addr *ma,
addr = buf_read_u32 (&buf, &status);
if (status)
{
buf_printf (&out, "%s", print_in_addr_t (addr, IA_EMPTY_IF_UNDEF, gc));
if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
buf_printf (&out, "ARP/");
buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
if (maddr.type & MR_WITH_NETBITS)
buf_printf (&out, "/%d", maddr.netbits);
{
if (flags & MAPF_SUBNET)
{
const in_addr_t netmask = netbits_to_netmask (maddr.netbits);
buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc));
}
else
buf_printf (&out, "/%d", maddr.netbits);
}
}
if (maddr.type & MR_WITH_PORT)
{

View File

@ -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 */

353
multi.c
View File

@ -35,6 +35,7 @@
#include "memdbg.h"
#include "forward-inline.h"
#include "pf-inline.h"
/*#define MULTI_DEBUG_EVENT_LOOP*/
@ -49,6 +50,16 @@ id (struct multi_instance *mi)
}
#endif
#ifdef MANAGEMENT_DEF_AUTH
static void
set_cc_config (struct multi_instance *mi, struct buffer_list *cc_config)
{
if (mi->cc_config)
buffer_list_free (mi->cc_config);
mi->cc_config = cc_config;
}
#endif
static bool
learn_address_script (const struct multi_context *m,
const struct multi_instance *mi,
@ -198,6 +209,25 @@ reap_buckets_per_pass (int n_buckets)
return constrain_int (n_buckets / REAP_DIVISOR, REAP_MIN, REAP_MAX);
}
#ifdef MANAGEMENT_DEF_AUTH
static uint32_t
cid_hash_function (const void *key, uint32_t iv)
{
const unsigned long *k = (const unsigned long *)key;
return (uint32_t) *k;
}
static bool
cid_compare_function (const void *key1, const void *key2)
{
const unsigned long *k1 = (const unsigned long *)key1;
const unsigned long *k2 = (const unsigned long *)key2;
return *k1 == *k2;
}
#endif
/*
* Main initialization function, init multi_context object.
*/
@ -252,6 +282,13 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa
mroute_addr_hash_function,
mroute_addr_compare_function);
#ifdef MANAGEMENT_DEF_AUTH
m->cid_hash = hash_init (t->options.real_hash_size,
0,
cid_hash_function,
cid_compare_function);
#endif
/*
* This is our scheduler, for time-based wakeup
* events.
@ -376,6 +413,15 @@ ungenerate_prefix (struct multi_instance *mi)
set_prefix (mi);
}
static const char *
mi_prefix (const struct multi_instance *mi)
{
if (mi && mi->msg_prefix)
return mi->msg_prefix;
else
return "UNDEF_I";
}
/*
* Tell the route helper about deleted iroutes so
* that it can update its mask of currently used
@ -439,6 +485,11 @@ multi_client_disconnect_script (struct multi_context *m,
gc_free (&gc);
}
#ifdef MANAGEMENT_DEF_AUTH
if (management)
management_notify_client_close (management, &mi->context.c2.mda_context, mi->context.c2.es);
#endif
}
}
@ -470,6 +521,12 @@ multi_close_instance (struct multi_context *m,
{
ASSERT (hash_remove (m->iter, &mi->real));
}
#ifdef MANAGEMENT_DEF_AUTH
if (mi->did_cid_hash)
{
ASSERT (hash_remove (m->cid_hash, &mi->context.c2.mda_context.cid));
}
#endif
schedule_remove_entry (m->schedule, (struct schedule_entry *) mi);
@ -487,6 +544,10 @@ multi_close_instance (struct multi_context *m,
mbuf_dereference_instance (m->mbuf, mi);
}
#ifdef MANAGEMENT_DEF_AUTH
set_cc_config (mi, NULL);
#endif
multi_client_disconnect_script (m, mi);
if (mi->did_open_context)
@ -538,6 +599,9 @@ multi_uninit (struct multi_context *m)
hash_free (m->hash);
hash_free (m->vhash);
hash_free (m->iter);
#ifdef MANAGEMENT_DEF_AUTH
hash_free (m->cid_hash);
#endif
m->hash = NULL;
schedule_free (m->schedule);
@ -608,6 +672,13 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real)
}
mi->did_iter = true;
#ifdef MANAGEMENT_DEF_AUTH
do {
mi->context.c2.mda_context.cid = m->cid_counter++;
} while (!hash_add (m->cid_hash, &mi->context.c2.mda_context.cid, mi, false));
mi->did_cid_hash = true;
#endif
mi->context.c2.push_reply_deferred = true;
if (!multi_process_post (m, mi, MPP_PRE_SELECT))
@ -983,7 +1054,8 @@ static struct multi_instance *
multi_learn_in_addr_t (struct multi_context *m,
struct multi_instance *mi,
in_addr_t a,
int netbits) /* -1 if host route, otherwise # of network bits in address */
int netbits, /* -1 if host route, otherwise # of network bits in address */
bool primary)
{
struct openvpn_sockaddr remote_si;
struct mroute_addr addr;
@ -998,7 +1070,15 @@ multi_learn_in_addr_t (struct multi_context *m,
addr.type |= MR_WITH_NETBITS;
addr.netbits = (uint8_t) netbits;
}
return multi_learn_addr (m, mi, &addr, 0);
{
struct multi_instance *owner = multi_learn_addr (m, mi, &addr, 0);
#ifdef MANAGEMENT_DEF_AUTH
if (management && owner)
management_learn_addr (management, &mi->context.c2.mda_context, &addr, primary);
#endif
return owner;
}
}
/*
@ -1028,7 +1108,7 @@ multi_add_iroutes (struct multi_context *m,
mroute_helper_add_iroute (m->route_helper, ir);
multi_learn_in_addr_t (m, mi, ir->network, ir->netbits);
multi_learn_in_addr_t (m, mi, ir->network, ir->netbits, false);
}
}
gc_free (&gc);
@ -1255,7 +1335,7 @@ multi_client_connect_post_plugin (struct multi_context *m,
for (i = 0; i < config.n; ++i)
{
if (config.list[i] && config.list[i]->value)
options_plugin_import (&mi->context.options,
options_string_import (&mi->context.options,
config.list[i]->value,
D_IMPORT_ERRORS|M_OPTERR,
option_permissions_mask,
@ -1276,6 +1356,46 @@ multi_client_connect_post_plugin (struct multi_context *m,
#endif
#ifdef MANAGEMENT_DEF_AUTH
/*
* Called to load management-derived client-connect config
*/
static void
multi_client_connect_mda (struct multi_context *m,
struct multi_instance *mi,
const struct buffer_list *config,
unsigned int option_permissions_mask,
unsigned int *option_types_found)
{
if (config)
{
struct buffer_entry *be;
for (be = config->head; be != NULL; be = be->next)
{
const char *opt = BSTR(&be->buf);
options_string_import (&mi->context.options,
opt,
D_IMPORT_ERRORS|M_OPTERR,
option_permissions_mask,
option_types_found,
mi->context.c2.es);
}
/*
* If the --client-connect script generates a config file
* with an --ifconfig-push directive, it will override any
* --ifconfig-push directive from the --client-config-dir
* directory or any --ifconfig-pool dynamic address.
*/
multi_select_virtual_addr (m, mi);
multi_set_virtual_addr_env (m, mi);
}
}
#endif
static void
multi_client_connect_setenv (struct multi_context *m,
struct multi_instance *mi)
@ -1468,6 +1588,17 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
cc_succeeded = false;
}
/*
* Check for client-connect script left by management interface client
*/
#ifdef MANAGEMENT_DEF_AUTH
if (cc_succeeded && mi->cc_config)
{
multi_client_connect_mda (m, mi, mi->cc_config, option_permissions_mask, &option_types_found);
++cc_succeeded_count;
}
#endif
/*
* Check for "disable" directive in client-config-dir file
* or config file generated by --client-connect script.
@ -1515,7 +1646,7 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
{
if (mi->context.c2.push_ifconfig_defined)
{
multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1);
multi_learn_in_addr_t (m, mi, mi->context.c2.push_ifconfig_local, -1, true);
msg (D_MULTI_LOW, "MULTI: primary virtual IP for %s: %s",
multi_instance_string (mi, false, &gc),
print_in_addr_t (mi->context.c2.push_ifconfig_local, 0, &gc));
@ -1553,6 +1684,11 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi
/* set flag so we don't get called again */
mi->connection_established_flag = true;
#ifdef MANAGEMENT_DEF_AUTH
if (management)
management_connection_established (management, &mi->context.c2.mda_context);
#endif
gc_free (&gc);
}
@ -1606,10 +1742,11 @@ multi_unicast (struct multi_context *m,
/*
* Broadcast a packet to all clients.
*/
void
static void
multi_bcast (struct multi_context *m,
const struct buffer *buf,
struct multi_instance *omit)
const struct multi_instance *sender_instance,
const struct mroute_addr *sender_addr)
{
struct hash_iterator hi;
struct hash_element *he;
@ -1628,8 +1765,34 @@ multi_bcast (struct multi_context *m,
while ((he = hash_iterator_next (&hi)))
{
mi = (struct multi_instance *) he->value;
if (mi != omit && !mi->halt)
multi_add_mbuf (m, mi, mb);
if (mi != sender_instance && !mi->halt)
{
#ifdef ENABLE_PF
if (sender_instance)
{
if (!pf_c2c_test (&sender_instance->context, &mi->context, "bcast_c2c"))
{
msg (D_PF_DROPPED_BCAST, "PF: client[%s] -> client[%s] packet dropped by BCAST packet filter",
mi_prefix (sender_instance),
mi_prefix (mi));
continue;
}
}
if (sender_addr)
{
if (!pf_addr_test (&mi->context, sender_addr, "bcast_src_addr"))
{
struct gc_arena gc = gc_new ();
msg (D_PF_DROPPED_BCAST, "PF: addr[%s] -> client[%s] packet dropped by BCAST packet filter",
mroute_addr_print_ex (sender_addr, MAPF_SHOW_ARP, &gc),
mi_prefix (mi));
gc_free (&gc);
continue;
}
}
#endif
multi_add_mbuf (m, mi, mb);
}
}
hash_iterator_free (&hi);
@ -1789,6 +1952,8 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
/* extract packet source and dest addresses */
mroute_flags = mroute_extract_addr_from_packet (&src,
&dest,
NULL,
NULL,
&c->c2.to_tun,
DEV_TYPE_TUN);
@ -1811,7 +1976,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
if (mroute_flags & MROUTE_EXTRACT_MCAST)
{
/* for now, treat multicast as broadcast */
multi_bcast (m, &c->c2.to_tun, m->pending);
multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
}
else /* possible client to client routing */
{
@ -1822,10 +1987,10 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
if (mi)
{
#ifdef ENABLE_PF
if (!pf_c2c_test (c, &mi->context))
if (!pf_c2c_test (c, &mi->context, "tun_c2c"))
{
msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
np (mi->msg_prefix));
msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TUN packet filter",
mi_prefix (mi));
}
else
#endif
@ -1838,18 +2003,29 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
}
}
#ifdef ENABLE_PF
else if (!pf_addr_test (c, &dest))
if (c->c2.to_tun.len && !pf_addr_test (c, &dest, "tun_dest_addr"))
{
msg (D_PF, "PF: client -> [%s] packet dropped by packet filter",
mroute_addr_print (&dest, &gc));
msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TUN packet filter",
mroute_addr_print_ex (&dest, MAPF_SHOW_ARP, &gc));
c->c2.to_tun.len = 0;
}
#endif
}
else if (TUNNEL_TYPE (m->top.c1.tuntap) == DEV_TYPE_TAP)
{
#ifdef ENABLE_PF
struct mroute_addr edest;
mroute_addr_reset (&edest);
#endif
/* extract packet source and dest addresses */
mroute_flags = mroute_extract_addr_from_packet (&src,
&dest,
NULL,
#ifdef ENABLE_PF
&edest,
#else
NULL,
#endif
&c->c2.to_tun,
DEV_TYPE_TAP);
@ -1862,7 +2038,7 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
{
if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
{
multi_bcast (m, &c->c2.to_tun, m->pending);
multi_bcast (m, &c->c2.to_tun, m->pending, NULL);
}
else /* try client-to-client routing */
{
@ -1871,12 +2047,30 @@ multi_process_incoming_link (struct multi_context *m, struct multi_instance *ins
/* if dest addr is a known client, route to it */
if (mi)
{
multi_unicast (m, &c->c2.to_tun, mi);
register_activity (c, BLEN(&c->c2.to_tun));
#ifdef ENABLE_PF
if (!pf_c2c_test (c, &mi->context, "tap_c2c"))
{
msg (D_PF_DROPPED, "PF: client -> client[%s] packet dropped by TAP packet filter",
mi_prefix (mi));
}
else
#endif
{
multi_unicast (m, &c->c2.to_tun, mi);
register_activity (c, BLEN(&c->c2.to_tun));
}
c->c2.to_tun.len = 0;
}
}
}
#ifdef ENABLE_PF
if (c->c2.to_tun.len && !pf_addr_test (c, &edest, "tap_dest_addr"))
{
msg (D_PF_DROPPED, "PF: client -> addr[%s] packet dropped by TAP packet filter",
mroute_addr_print_ex (&edest, MAPF_SHOW_ARP, &gc));
c->c2.to_tun.len = 0;
}
#endif
}
else
{
@ -1918,6 +2112,20 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
struct mroute_addr src, dest;
const int dev_type = TUNNEL_TYPE (m->top.c1.tuntap);
#ifdef ENABLE_PF
struct mroute_addr esrc, *e1, *e2;
if (dev_type == DEV_TYPE_TUN)
{
e1 = NULL;
e2 = &src;
}
else
{
e1 = e2 = &esrc;
mroute_addr_reset (&esrc);
}
#endif
#ifdef MULTI_DEBUG_EVENT_LOOP
printf ("TUN -> TCP/UDP [%d]\n", BLEN (&m->top.c2.buf));
#endif
@ -1932,6 +2140,12 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
mroute_flags = mroute_extract_addr_from_packet (&src,
&dest,
#ifdef ENABLE_PF
e1,
#else
NULL,
#endif
NULL,
&m->top.c2.buf,
dev_type);
@ -1943,7 +2157,11 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
if (mroute_flags & (MROUTE_EXTRACT_BCAST|MROUTE_EXTRACT_MCAST))
{
/* for now, treat multicast as broadcast */
multi_bcast (m, &m->top.c2.buf, NULL);
#ifdef ENABLE_PF
multi_bcast (m, &m->top.c2.buf, NULL, e2);
#else
multi_bcast (m, &m->top.c2.buf, NULL, NULL);
#endif
}
else
{
@ -1957,10 +2175,10 @@ multi_process_incoming_tun (struct multi_context *m, const unsigned int mpp_flag
set_prefix (m->pending);
#ifdef ENABLE_PF
if (!pf_addr_test (c, &src))
if (!pf_addr_test (c, e2, "tun_tap_src_addr"))
{
msg (D_PF, "PF: [%s] -> client packet dropped by packet filter",
mroute_addr_print (&src, &gc));
msg (D_PF_DROPPED, "PF: addr[%s] -> client packet dropped by packet filter",
mroute_addr_print_ex (&src, MAPF_SHOW_ARP, &gc));
buf_reset_len (&c->c2.buf);
}
else
@ -2111,7 +2329,7 @@ gremlin_flood_clients (struct multi_context *m)
ASSERT (buf_write_u8 (&buf, get_random () & 0xFF));
for (i = 0; i < parm.n_packets; ++i)
multi_bcast (m, &buf, NULL);
multi_bcast (m, &buf, NULL, NULL);
gc_free (&gc);
}
@ -2281,6 +2499,86 @@ management_delete_event (void *arg, event_t event)
#endif
#ifdef MANAGEMENT_DEF_AUTH
static struct multi_instance *
lookup_by_cid (struct multi_context *m, const unsigned long cid)
{
if (m)
{
struct multi_instance *mi = (struct multi_instance *) hash_lookup (m->cid_hash, &cid);
if (mi && !mi->halt)
return mi;
}
return NULL;
}
static bool
management_kill_by_cid (void *arg, const unsigned long cid)
{
struct multi_context *m = (struct multi_context *) arg;
struct multi_instance *mi = lookup_by_cid (m, cid);
if (mi)
{
multi_signal_instance (m, mi, SIGTERM);
return true;
}
else
return false;
}
static bool
management_client_auth (void *arg,
const unsigned long cid,
const unsigned int mda_key_id,
const bool auth,
const char *reason,
struct buffer_list *cc_config) /* ownership transferred */
{
struct multi_context *m = (struct multi_context *) arg;
struct multi_instance *mi = lookup_by_cid (m, cid);
bool cc_config_owned = true;
bool ret = false;
if (mi)
{
ret = tls_authenticate_key (mi->context.c2.tls_multi, mda_key_id, auth);
if (ret)
{
if (auth && !mi->connection_established_flag)
{
set_cc_config (mi, cc_config);
cc_config_owned = false;
}
if (!auth && reason)
msg (D_MULTI_LOW, "MULTI: connection rejected: %s", reason);
}
}
if (cc_config_owned && cc_config)
buffer_list_free (cc_config);
return ret;
}
#endif
#ifdef MANAGEMENT_PF
static bool
management_client_pf (void *arg,
const unsigned long cid,
struct buffer_list *pf_config) /* ownership transferred */
{
struct multi_context *m = (struct multi_context *) arg;
struct multi_instance *mi = lookup_by_cid (m, cid);
bool ret = false;
if (mi && pf_config)
ret = pf_load_from_buffer_list (&mi->context, pf_config);
if (pf_config)
buffer_list_free (pf_config);
return ret;
}
#endif
void
init_management_callback_multi (struct multi_context *m)
{
@ -2295,6 +2593,13 @@ init_management_callback_multi (struct multi_context *m)
cb.kill_by_cn = management_callback_kill_by_cn;
cb.kill_by_addr = management_callback_kill_by_addr;
cb.delete_event = management_delete_event;
#ifdef MANAGEMENT_DEF_AUTH
cb.kill_by_cid = management_kill_by_cid;
cb.client_auth = management_client_auth;
#endif
#ifdef MANAGEMENT_PF
cb.client_pf = management_client_pf;
#endif
management_set_callback (management, &cb);
}
#endif

13
multi.h
View File

@ -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
*/

View File

@ -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,

View File

@ -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
};
/*

View File

@ -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;

View File

@ -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

59
pf-inline.h Normal file
View File

@ -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

276
pf.c
View File

@ -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;
}
if (netbits < 0 || netbits > 32)
{
msg (D_PF, "PF: %s/%d: bad '/n' subnet specifier: must be between 0 and 32: '%s'", fn, line_num, div);
*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 (openvpn_inet_aton (line, &network) != OIA_IP)
{
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, "--------------------");

23
pf.h
View File

@ -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);
#ifdef MANAGEMENT_PF
bool pf_load_from_buffer_list (struct context *c, const struct buffer_list *config);
#endif
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;
}
#if PF_DEBUG >= 1
#ifdef ENABLE_DEBUG
void pf_context_print (const struct pf_context *pfc, const char *prefix, const int lev);
#endif

22
proto.h
View File

@ -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

174
ssl.c
View File

@ -860,6 +860,26 @@ tls_lock_common_name (struct tls_multi *multi)
}
#ifdef ENABLE_DEF_AUTH
/* key_state_test_auth_control_file return values,
NOTE: acf_merge indexing depends on these values */
#define ACF_UNDEFINED 0
#define ACF_SUCCEEDED 1
#define ACF_DISABLED 2
#define ACF_FAILED 3
#endif
#ifdef MANAGEMENT_DEF_AUTH
static inline unsigned int
man_def_auth_test (const struct key_state *ks)
{
if (management_enable_def_auth (management))
return ks->mda_status;
else
return ACF_DISABLED;
}
#endif
#ifdef PLUGIN_DEF_AUTH
/*
* auth_control_file functions
@ -890,17 +910,12 @@ key_state_gen_auth_control_file (struct key_state *ks, const struct tls_options
gc_free (&gc);
}
/* key_state_test_auth_control_file return values */
#define ACF_UNDEFINED 0
#define ACF_SUCCEEDED 1
#define ACF_DISABLED 2
#define ACF_FAILED 3
static int
static unsigned int
key_state_test_auth_control_file (struct key_state *ks)
{
if (ks && ks->auth_control_file)
{
int ret = ks->auth_control_status;
unsigned int ret = ks->auth_control_status;
if (ret == ACF_UNDEFINED)
{
FILE *fp = fopen (ks->auth_control_file, "r");
@ -935,14 +950,37 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
bool active = false;
#ifdef ENABLE_DEF_AUTH
if (latency && multi->tas_last && multi->tas_last + latency >= now)
return TLS_AUTHENTICATION_UNDEFINED;
multi->tas_last = now;
static const unsigned char acf_merge[] =
{
ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_UNDEFINED */
ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_SUCCEEDED */
ACF_UNDEFINED, /* s1=ACF_UNDEFINED s2=ACF_DISABLED */
ACF_FAILED, /* s1=ACF_UNDEFINED s2=ACF_FAILED */
ACF_UNDEFINED, /* s1=ACF_SUCCEEDED s2=ACF_UNDEFINED */
ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_SUCCEEDED */
ACF_SUCCEEDED, /* s1=ACF_SUCCEEDED s2=ACF_DISABLED */
ACF_FAILED, /* s1=ACF_SUCCEEDED s2=ACF_FAILED */
ACF_UNDEFINED, /* s1=ACF_DISABLED s2=ACF_UNDEFINED */
ACF_SUCCEEDED, /* s1=ACF_DISABLED s2=ACF_SUCCEEDED */
ACF_DISABLED, /* s1=ACF_DISABLED s2=ACF_DISABLED */
ACF_FAILED, /* s1=ACF_DISABLED s2=ACF_FAILED */
ACF_FAILED, /* s1=ACF_FAILED s2=ACF_UNDEFINED */
ACF_FAILED, /* s1=ACF_FAILED s2=ACF_SUCCEEDED */
ACF_FAILED, /* s1=ACF_FAILED s2=ACF_DISABLED */
ACF_FAILED /* s1=ACF_FAILED s2=ACF_FAILED */
};
#endif
if (multi)
{
int i;
#ifdef ENABLE_DEF_AUTH
if (latency && multi->tas_last && multi->tas_last + latency >= now)
return TLS_AUTHENTICATION_UNDEFINED;
multi->tas_last = now;
#endif
for (i = 0; i < KEY_SCAN_SIZE; ++i)
{
struct key_state *ks = multi->key_scan[i];
@ -952,7 +990,16 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
if (ks->authenticated)
{
#ifdef ENABLE_DEF_AUTH
switch (key_state_test_auth_control_file (ks))
unsigned int s1 = ACF_DISABLED;
unsigned int s2 = ACF_DISABLED;
#ifdef PLUGIN_DEF_AUTH
s1 = key_state_test_auth_control_file (ks);
#endif
#ifdef MANAGEMENT_DEF_AUTH
s2 = man_def_auth_test (ks);
#endif
ASSERT (s1 < 4 && s2 < 4);
switch (acf_merge[(s1<<2) + s2])
{
case ACF_SUCCEEDED:
case ACF_DISABLED:
@ -989,6 +1036,28 @@ tls_authentication_status (struct tls_multi *multi, const int latency)
return TLS_AUTHENTICATION_FAILED;
}
#ifdef MANAGEMENT_DEF_AUTH
bool
tls_authenticate_key (struct tls_multi *multi, const unsigned int mda_key_id, const bool auth)
{
bool ret = false;
if (multi)
{
int i;
for (i = 0; i < KEY_SCAN_SIZE; ++i)
{
struct key_state *ks = multi->key_scan[i];
if (ks->mda_key_id == mda_key_id)
{
ks->mda_status = auth ? ACF_SUCCEEDED : ACF_FAILED;
ret = true;
}
}
}
return ret;
}
#endif
void
tls_deauthenticate (struct tls_multi *multi)
{
@ -1458,7 +1527,7 @@ init_ssl (const struct options *options)
#if P2MP_SERVER
if (options->client_cert_not_required)
{
msg (M_WARN, "WARNING: This configuration may accept clients which do not present a certificate");
msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION --client-cert-not-required may accept clients which do not present a certificate");
}
else
#endif
@ -1976,6 +2045,10 @@ key_state_init (struct tls_session *session, struct key_state *ks)
packet_id_init (&ks->packet_id,
session->opt->replay_window,
session->opt->replay_time);
#ifdef MANAGEMENT_DEF_AUTH
ks->mda_key_id = session->opt->mda_context->mda_key_id_counter++;
#endif
}
static void
@ -2018,7 +2091,7 @@ key_state_free (struct key_state *ks, bool clear)
packet_id_free (&ks->packet_id);
#ifdef ENABLE_DEF_AUTH
#ifdef PLUGIN_DEF_AUTH
key_state_rm_auth_control_file (ks);
#endif
@ -2933,7 +3006,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
/* setenv client real IP address */
setenv_untrusted (session);
#ifdef ENABLE_DEF_AUTH
#ifdef PLUGIN_DEF_AUTH
/* generate filename for deferred auth control file */
key_state_gen_auth_control_file (ks, session->opt);
#endif
@ -2941,7 +3014,7 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
/* call command */
retval = plugin_call (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, NULL, NULL, session->opt->es);
#ifdef ENABLE_DEF_AUTH
#ifdef PLUGIN_DEF_AUTH
/* purge auth control filename (and file itself) for non-deferred returns */
if (retval != OPENVPN_PLUGIN_FUNC_DEFERRED)
key_state_rm_auth_control_file (ks);
@ -2952,12 +3025,56 @@ verify_user_pass_plugin (struct tls_session *session, const struct user_pass *up
}
else
{
msg (D_TLS_ERRORS, "TLS Auth Error: peer provided a blank username");
msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_plugin): peer provided a blank username");
}
return retval;
}
/*
* MANAGEMENT_DEF_AUTH internal ssl.c status codes
*/
#define KMDA_ERROR 0
#define KMDA_SUCCESS 1
#define KMDA_UNDEF 2
#define KMDA_DEF 3
#ifdef MANAGEMENT_DEF_AUTH
static int
verify_user_pass_management (struct tls_session *session, const struct user_pass *up, const char *raw_username)
{
int retval = KMDA_ERROR;
/* Is username defined? */
if (strlen (up->username))
{
/* set username/password in private env space */
setenv_str (session->opt->es, "username", raw_username);
setenv_str (session->opt->es, "password", up->password);
/* setenv incoming cert common name for script */
setenv_str (session->opt->es, "common_name", session->common_name);
/* setenv client real IP address */
setenv_untrusted (session);
if (management)
management_notify_client_needing_auth (management, ks->mda_key_id, session->opt->mda_context, session->opt->es);
setenv_del (session->opt->es, "password");
setenv_str (session->opt->es, "username", up->username);
retval = KMDA_SUCCESS;
}
else
{
msg (D_TLS_ERRORS, "TLS Auth Error (verify_user_pass_management): peer provided a blank username");
}
return retval;
}
#endif
/*
* Handle the reading and writing of key data to and from
* the TLS control channel (cleartext).
@ -3134,6 +3251,13 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
char *options;
struct user_pass *up;
bool man_def_auth = KMDA_UNDEF;
#ifdef MANAGEMENT_DEF_AUTH
if (management_enable_def_auth (management))
man_def_auth = KMDA_DEF;
#endif
ASSERT (session->opt->key_method == 2);
/* allocate temporary objects */
@ -3169,7 +3293,8 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
/* should we check username/password? */
ks->authenticated = false;
if (session->opt->auth_user_pass_verify_script
|| plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
|| plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY)
|| man_def_auth == KMDA_DEF)
{
int s1 = OPENVPN_PLUGIN_FUNC_SUCCESS;
bool s2 = true;
@ -3195,6 +3320,10 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
string_mod (up->password, CC_PRINT, CC_CRLF, '_');
/* call plugin(s) and/or script */
#ifdef MANAGEMENT_DEF_AUTH
if (man_def_auth == KMDA_DEF)
man_def_auth = verify_user_pass_management (session, up, raw_username);
#endif
if (plugin_defined (session->opt->plugins, OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY))
s1 = verify_user_pass_plugin (session, up, raw_username);
if (session->opt->auth_user_pass_verify_script)
@ -3202,16 +3331,21 @@ key_method_2_read (struct buffer *buf, struct tls_multi *multi, struct tls_sessi
/* auth succeeded? */
if ((s1 == OPENVPN_PLUGIN_FUNC_SUCCESS
#ifdef ENABLE_DEF_AUTH
#ifdef PLUGIN_DEF_AUTH
|| s1 == OPENVPN_PLUGIN_FUNC_DEFERRED
#endif
) && s2)
) && s2 && man_def_auth != KMDA_ERROR)
{
ks->authenticated = true;
#ifdef ENABLE_DEF_AUTH
#ifdef PLUGIN_DEF_AUTH
if (s1 == OPENVPN_PLUGIN_FUNC_DEFERRED)
ks->auth_deferred = true;
#endif
#ifdef MANAGEMENT_DEF_AUTH
if (man_def_auth != KMDA_UNDEF)
ks->auth_deferred = true;
#endif
if (session->opt->username_as_common_name)
set_common_name (session, up->username);
msg (D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",

16
ssl.h
View File

@ -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
*/

View File

@ -471,19 +471,40 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
* Enable deferred authentication
* Enable deferred authentication?
*/
#if defined(ENABLE_PLUGIN) && P2MP_SERVER
#define CONFIGURE_DEF_AUTH /* this should be set by autoconf and config.h */
#if defined(CONFIGURE_DEF_AUTH) && defined(P2MP_SERVER) && defined(ENABLE_PLUGIN)
#define PLUGIN_DEF_AUTH
#endif
#if defined(CONFIGURE_DEF_AUTH) && defined(P2MP_SERVER) && defined(ENABLE_MANAGEMENT)
#define MANAGEMENT_DEF_AUTH
#endif
#if defined(PLUGIN_DEF_AUTH) || defined(MANAGEMENT_DEF_AUTH)
#define ENABLE_DEF_AUTH
#endif
/*
* Enable packet filter
* Enable packet filter?
*/
#if defined(ENABLE_PLUGIN) && P2MP_SERVER && defined(HAVE_STAT)
#define CONFIGURE_PF /* this should be set by autoconf and config.h */
#if defined(CONFIGURE_PF) && defined(P2MP_SERVER) && defined(ENABLE_PLUGIN) && defined(HAVE_STAT)
#define PLUGIN_PF
#endif
#if defined(CONFIGURE_PF) && defined(P2MP_SERVER) && defined(MANAGEMENT_DEF_AUTH)
#define MANAGEMENT_PF
#endif
#if defined(PLUGIN_PF) || defined(MANAGEMENT_PF)
#define ENABLE_PF
#endif
/*
* Don't compile the struct buffer_list code unless something needs it
*/
#if defined(ENABLE_MANAGEMENT) || defined(ENABLE_PF)
#define ENABLE_BUFFER_LIST
#endif
/*
* Do we have pthread capability?
*/

2
tun.c
View File

@ -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);
}

View File

@ -1,5 +1,5 @@
dnl define the OpenVPN version
define(PRODUCT_VERSION,[2.1_rc7d])
define(PRODUCT_VERSION,[2.1_rc7e])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])