diff options
Diffstat (limited to 'src/openvpn/multi.c')
-rw-r--r-- | src/openvpn/multi.c | 155 |
1 files changed, 152 insertions, 3 deletions
diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c index 902c4dc..05c36db 100644 --- a/src/openvpn/multi.c +++ b/src/openvpn/multi.c @@ -28,6 +28,11 @@ #include "config-msvc.h" #endif +#ifdef HAVE_SYS_INOTIFY_H +#include <sys/inotify.h> +#define INOTIFY_EVENT_BUFFER_SIZE 16384 +#endif + #include "syshead.h" #if P2MP_SERVER @@ -243,6 +248,23 @@ cid_compare_function (const void *key1, const void *key2) #endif +#ifdef ENABLE_ASYNC_PUSH +static uint32_t +/* + * inotify watcher descriptors are used as hash value + */ +int_hash_function (const void *key, uint32_t iv) +{ + return (unsigned long)key; +} + +static bool +int_compare_function (const void *key1, const void *key2) +{ + return (unsigned long)key1 == (unsigned long)key2; +} +#endif + /* * Main initialization function, init multi_context object. */ @@ -304,6 +326,17 @@ multi_init (struct multi_context *m, struct context *t, bool tcp_mode, int threa cid_compare_function); #endif +#ifdef ENABLE_ASYNC_PUSH + /* + * Mapping between inotify watch descriptors and + * multi_instances. + */ + m->inotify_watchers = hash_init (t->options.real_hash_size, + get_random(), + int_hash_function, + int_compare_function); +#endif + /* * This is our scheduler, for time-based wakeup * events. @@ -562,6 +595,14 @@ multi_close_instance (struct multi_context *m, } #endif +#ifdef ENABLE_ASYNC_PUSH + if (mi->inotify_watch != -1) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch); + mi->inotify_watch = -1; + } +#endif + m->instances[mi->context.c2.tls_multi->peer_id] = NULL; schedule_remove_entry (m->schedule, (struct schedule_entry *) mi); @@ -642,6 +683,11 @@ multi_uninit (struct multi_context *m) free(m->instances); +#ifdef ENABLE_ASYNC_PUSH + hash_free (m->inotify_watchers); + m->inotify_watchers = NULL; +#endif + schedule_free (m->schedule); mbuf_free (m->mbuf); ifconfig_pool_free (m->ifconfig_pool); @@ -718,6 +764,11 @@ multi_create_instance (struct multi_context *m, const struct mroute_addr *real) mi->context.c2.push_reply_deferred = true; +#ifdef ENABLE_ASYNC_PUSH + mi->context.c2.push_request_received = false; + mi->inotify_watch = -1; +#endif + if (!multi_process_post (m, mi, MPP_PRE_SELECT)) { msg (D_MULTI_ERRORS, "MULTI: signal occurred during client instance initialization"); @@ -923,6 +974,13 @@ multi_print_status (struct multi_context *m, struct status_output *so, const int status_flush (so); gc_free (&gc_top); } + +#ifdef ENABLE_ASYNC_PUSH + if (m->inotify_watchers) + { + msg (D_MULTI_DEBUG, "inotify watchers count: %d\n", hash_n_elements(m->inotify_watchers)); + } +#endif } /* @@ -1877,6 +1935,14 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi /* set context-level authentication flag */ mi->context.c2.context_auth = CAS_SUCCEEDED; + +#ifdef ENABLE_ASYNC_PUSH + /* authentication complete, send push reply */ + if (mi->context.c2.push_request_received) + { + process_incoming_push_request(&mi->context); + } +#endif } else { @@ -1906,6 +1972,58 @@ multi_connection_established (struct multi_context *m, struct multi_instance *mi mi->context.c2.push_reply_deferred = false; } +#ifdef ENABLE_ASYNC_PUSH +/* + * Called when inotify event is fired, which happens when acf file is closed or deleted. + * Continues authentication and sends push_reply. + */ +void +multi_process_file_closed (struct multi_context *m, const unsigned int mpp_flags) +{ + char buffer[INOTIFY_EVENT_BUFFER_SIZE]; + size_t buffer_i = 0; + int r = read (m->top.c2.inotify_fd, buffer, INOTIFY_EVENT_BUFFER_SIZE); + + while (buffer_i < r) + { + /* parse inotify events */ + struct inotify_event *pevent = (struct inotify_event *) &buffer[buffer_i]; + size_t event_size = sizeof (struct inotify_event) + pevent->len; + buffer_i += event_size; + + msg(D_MULTI_DEBUG, "MULTI: modified fd %d, mask %d", pevent->wd, pevent->mask); + + struct multi_instance* mi = hash_lookup(m->inotify_watchers, (void*) (unsigned long) pevent->wd); + + if (pevent->mask & IN_CLOSE_WRITE) + { + if (mi) + { + /* continue authentication and send push_reply */ + multi_process_post (m, mi, mpp_flags); + } + else + { + msg(D_MULTI_ERRORS, "MULTI: multi_instance not found!"); + } + } + else if (pevent->mask & IN_IGNORED) + { + /* this event is _always_ fired when watch is removed or file is deleted */ + if (mi) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long) pevent->wd); + mi->inotify_watch = -1; + } + } + else + { + msg(D_MULTI_ERRORS, "MULTI: unknown mask %d", pevent->mask); + } + } +} +#endif + /* * Add a mbuf buffer to a particular * instance. @@ -2066,19 +2184,50 @@ multi_process_post (struct multi_context *m, struct multi_instance *mi, const un if (!IS_SIG (&mi->context) && ((flags & MPP_PRE_SELECT) || ((flags & MPP_CONDITIONAL_PRE_SELECT) && !ANY_OUT (&mi->context)))) { +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) + bool was_authenticated = false; + struct key_state *ks = NULL; + if (mi->context.c2.tls_multi) + { + ks = &mi->context.c2.tls_multi->session[TM_ACTIVE].key[KS_PRIMARY]; + was_authenticated = ks->authenticated; + } +#endif + /* figure timeouts and fetch possible outgoing to_link packets (such as ping or TLS control) */ pre_select (&mi->context); - if (!IS_SIG (&mi->context)) +#if defined(ENABLE_ASYNC_PUSH) && defined(ENABLE_DEF_AUTH) + if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated) { - /* tell scheduler to wake us up at some point in the future */ - multi_schedule_context_wakeup(m, mi); + /* watch acf file */ + long watch_descriptor = inotify_add_watch(m->top.c2.inotify_fd, ks->auth_control_file, IN_CLOSE_WRITE | IN_ONESHOT); + if (watch_descriptor >= 0) + { + if (mi->inotify_watch != -1) + { + hash_remove(m->inotify_watchers, (void*) (unsigned long)mi->inotify_watch); + } + hash_add (m->inotify_watchers, (const uintptr_t*)watch_descriptor, mi, true); + mi->inotify_watch = watch_descriptor; + } + else + { + msg(M_NONFATAL, "MULTI: inotify_add_watch error: %s", strerror(errno)); + } + } +#endif + if (!IS_SIG (&mi->context)) + { /* connection is "established" when SSL/TLS key negotiation succeeds and (if specified) auth user/pass succeeds */ if (!mi->connection_established_flag && CONNECTION_ESTABLISHED (&mi->context)) multi_connection_established (m, mi); + + /* tell scheduler to wake us up at some point in the future */ + multi_schedule_context_wakeup(m, mi); } } |