Deferred client-connect patch set
v3 cover Deferred client-connect patch set
Fabian submitted the original patch set in 2015. When I started working on it I had quite big rebase conflicts and decided to rebsae it manually. During this rebase I adjusted the code and made other minor and major adjustments to the code to better fit our current code style. The minor adjustments are mostly not mentioned in the commit message. The larger changes are mentioned or if feasible and sensible in their own commits.
Arne Schwabe (5):
client-connect: Change connection_established_flag from bool to enum
client-connect: Move adding inotify watch into its own function
client-connect: Also use inotify for the deferred client-connect
status file
Implement deferred connect support for plugin API v2
Add documentation for the deferred client connect feature
Fabian Knittel (8):
client-connect: Split multi_connection_established into separate
functions
client-connect: Refactor multi_client_connect_source_ccd
client-connect: Move multi_client_connect_setenv into early_setup
client-connect: Refactor to use return values instead of modifying a
passed-in flag
client-connect: Refactor client-connect handling to calling a bunch of
hooks in a loop
client-connect: Add CC_RET_DEFERRED and cope with deferred
client-connect
client-connect: Add deferred support to the client-connect script
handler
client-connect: Add deferred support to the client-connect plugin v1
handler
doc/openvpn.8 | 47 +- include/openvpn-plugin.h.in | 51 +- src/openvpn/multi.c | 1032 ++++++++++++++++++++++++++--------- src/openvpn/multi.h | 52 +- src/openvpn/plugin.c | 6 + 5 files changed, 891 insertions(+), 297 deletions(-)
Signature · 1 line
2.19.1
PATCH v3 1/13 client-connect: Split multi_connection_established into separate functions
From: Fabian Knittel <[email protected]...>
This patch splits up the multi_connection_established() function. Each new helper function does a specific job. Functions that do a similar job receive a similar calling interface.
The patch tries not to reindent code, so that the real changes are as clearly visible as possible. (A follow-up patch will only do indentation changes.)
PATCH v3: Since the code has changed enough from the time the original patch to the current master, the splitting has been redone from the current code. Also some style and minor code changes have been added doing this patch. This elimininates and the big reformatting done before eliminates the follow up patch with indentation changes.
The original patch already replaces some instances of option_permission_mask with CLIENT_CONNECT_OPT_MASK. The V3 version does this more consequently.
--- src/openvpn/multi.c | 598 +++++++++++++++++++++++++------------------- src/openvpn/multi.h | 4 +- 2 files changed, 347 insertions(+), 255 deletions(-)
src/openvpn/multi.c +344 -254
@@ -1638,7 +1638,6 @@ static void multi_client_connect_post(struct multi_context *m, struct multi_instance *mi, const char *dc_file, - unsigned int option_permissions_mask, unsigned int *option_types_found) {/* Did script generate a dynamic config file? */
@@ -1647,7 +1646,7 @@ multi_client_connect_post(struct multi_context *m,options_server_import (&mi->context.options, dc_file,D_IMPORT_ERRORS |M_OPTERR , - option_permissions_mask, +CLIENT_CONNECT_OPT_MASK , option_types_found, mi->context.c2.es);
@@ -1671,7 +1670,6 @@ static void multi_client_connect_post_plugin(struct multi_context *m, struct multi_instance *mi, const struct plugin_return *pr, - unsigned int option_permissions_mask, unsigned int *option_types_found) { struct plugin_return config;
@@ -1689,7 +1687,7 @@ multi_client_connect_post_plugin(struct multi_context *m,options_string_import (&mi->context.options, config.list[i]->value,D_IMPORT_ERRORS |M_OPTERR , - option_permissions_mask, +CLIENT_CONNECT_OPT_MASK , option_types_found, mi->context.c2.es); }
@@ -1716,21 +1714,19 @@ multi_client_connect_post_plugin(struct multi_context *m,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) +if (mi->cc_config) {struct buffer_entry *be; -for (be = config->head; be !=NULL ; be = be->next) +for (be = mi->cc_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, +CLIENT_CONNECT_OPT_MASK , option_types_found, mi->context.c2.es); }
@@ -1773,215 +1769,387 @@ multi_client_connect_setenv(struct multi_context *m,gc_free (&gc); } -/* -* Called as soon as the SSL/TLS connection authenticates. -* -* Instance-specific directives to be processed: -* -* iroute start-ip end-ip -* ifconfig-push local remote-netmask -* push -*/ static void -multi_connection_established (struct multi_context *m,struct multi_instance *mi) +multi_client_connect_call_plugin_v1 (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found, +int *cc_succeeded, +int *cc_succeeded_count) { -if (tls_authentication_status (mi->context.c2.tls_multi,0 ) ==TLS_AUTHENTICATION_SUCCEEDED ) +#ifdef ENABLE_PLUGIN +ASSERT (m); +ASSERT (mi); +ASSERT (option_types_found); +ASSERT (cc_succeeded); +ASSERT (cc_succeeded_count); + +/* deprecated callback, use a file for passing back return info */ +if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT )) { +struct argv argv =argv_new ();struct gc_arena gc =gc_new (); -unsigned int option_types_found =0 ; +const char *dc_file = +platform_create_temp_file (mi->context.options.tmp_dir,"cc" , &gc); -const unsigned int option_permissions_mask = -OPT_P_INSTANCE - |OPT_P_INHERIT - |OPT_P_PUSH - |OPT_P_TIMER - |OPT_P_CONFIG - |OPT_P_ECHO - |OPT_P_COMP - |OPT_P_SOCKFLAGS ; +if (!dc_file) + { + cc_succeeded = false; + goto cleanup; + } -int cc_succeeded = true;/* client connect script status */ -int cc_succeeded_count =0 ; +argv_printf (&argv,"%s" , dc_file); +if (plugin_call (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT , + &argv,NULL , mi->context.c2.es) + !=OPENVPN_PLUGIN_FUNC_SUCCESS ) + { +msg (M_WARN ,"WARNING: client-connect plugin call failed" ); + *cc_succeeded = false; + } +else + { +multi_client_connect_post (m, mi, dc_file, option_types_found); + (*cc_succeeded_count)++; + } + +if (!platform_unlink (dc_file)) + { +msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , + dc_file); + } + + cleanup: +argv_reset (&argv); +gc_free (&gc); + } +#endif /* ifdef ENABLE_PLUGIN */ +} -ASSERT (mi->context.c1.tuntap); +static void +multi_client_connect_call_plugin_v2 (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found, +int *cc_succeeded, +int *cc_succeeded_count) +{ +#ifdef ENABLE_PLUGIN +ASSERT (m); +ASSERT (mi); +ASSERT (option_types_found); +ASSERT (cc_succeeded); +ASSERT (cc_succeeded_count); -/* lock down the common name and cert hashes so they can't change during future TLS renegotiations */ -tls_lock_common_name (mi->context.c2.tls_multi); -tls_lock_cert_hash_set (mi->context.c2.tls_multi); +/* V2 callback, use a plugin_return struct for passing back return info */ +if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 )) + { +struct plugin_return pr; -/* generate a msg() prefix for this client instance */ -generate_prefix (mi); +plugin_return_init (&pr); -/* delete instances of previous clients with same common-name */ -if (!mi->context.options.duplicate_cn) +if (plugin_call (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 , +NULL , &pr, mi->context.c2.es) + !=OPENVPN_PLUGIN_FUNC_SUCCESS ) { -multi_delete_dup (m, mi); +msg (M_WARN ,"WARNING: client-connect-v2 plugin call failed" ); + *cc_succeeded = false; + } +else + { +multi_client_connect_post_plugin (m, mi, &pr, option_types_found); + (*cc_succeeded_count)++; } -/* reset pool handle to null */ - mi->vaddr_handle =-1 ; +plugin_return_free (&pr); + } +#endif /* ifdef ENABLE_PLUGIN */ +} -/* -* Try to source a dynamic config file from the -* --client-config-dir directory. -*/ -if (mi->context.options.client_config_dir) + + +/** +* Runs the --client-connect script if one is defined. +*/ +static void +multi_client_connect_call_script (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found, +int *cc_succeeded, +int *cc_succeeded_count) +{ +if (mi->context.options.client_connect_script) + { +struct argv argv =argv_new (); +struct gc_arena gc =gc_new (); +const char *dc_file =NULL ; + +setenv_str (mi->context.c2.es,"script_type" ,"client-connect" ); + + dc_file =platform_create_temp_file (mi->context.options.tmp_dir, +"cc" , &gc); +if (!dc_file) { -const char *ccd_file; + *cc_succeeded = false; + goto cleanup; + } - ccd_file =platform_gen_path (mi->context.options.client_config_dir, -tls_common_name (mi->context.c2.tls_multi, - false), - &gc); +argv_parse_cmd (&argv, mi->context.options.client_connect_script); +argv_printf_cat (&argv,"%s" , dc_file); -/* try common-name file */ -if (platform_test_file (ccd_file)) - { -options_server_import (&mi->context.options, - ccd_file, -D_IMPORT_ERRORS |M_OPTERR , - option_permissions_mask, - &option_types_found, - mi->context.c2.es); - } -else /* try default file */ - { - ccd_file =platform_gen_path (mi->context.options.client_config_dir, -CCD_DEFAULT , - &gc); +if (openvpn_run_script (&argv, mi->context.c2.es,0 ,"--client-connect" )) + { +multi_client_connect_post (m, mi, dc_file, option_types_found); + (*cc_succeeded_count)++; + } +else + { + *cc_succeeded = false; + } -if (platform_test_file (ccd_file)) - { -options_server_import (&mi->context.options, - ccd_file, -D_IMPORT_ERRORS |M_OPTERR , - option_permissions_mask, - &option_types_found, - mi->context.c2.es); - } - } +if (!platform_unlink (dc_file)) + { +msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , + dc_file); } + cleanup: +argv_reset (&argv); +gc_free (&gc); + } +} -/* -* Select a virtual address from either --ifconfig-push in --client-config-dir file -* or --ifconfig-pool. -*/ -multi_select_virtual_addr (m, mi); +static void +multi_client_connect_late_setup (struct multi_context *m, +struct multi_instance *mi, +const unsigned int option_types_found) +{ +struct gc_arena gc =gc_new (); +/* +* Process sourced options. +*/ +do_deferred_options (&mi->context, option_types_found); -/* do --client-connect setenvs */ -multi_client_connect_setenv (m, mi); +/* +* make sure we got ifconfig settings from somewhere +*/ +if (!mi->context.c2.push_ifconfig_defined) + { +msg (D_MULTI_ERRORS ,"MULTI: no dynamic or static remote" +"--ifconfig address is available for %s" , +multi_instance_string (mi, false, &gc)); + } + +/* +* make sure that ifconfig settings comply with constraints +*/ +if (!ifconfig_push_constraint_satisfied (&mi->context)) + { +const char * ifconfig_constraint_network = +print_in_addr_t (mi->context.options.push_ifconfig_constraint_network,0 , &gc); +const char * ifconfig_constraint_netmask = +print_in_addr_t (mi->context.options.push_ifconfig_constraint_netmask,0 , &gc); + +/* JYFIXME -- this should cause the connection to fail */ +msg (D_MULTI_ERRORS ,"MULTI ERROR: primary virtual IP for %s (%s)" +"violates tunnel network/netmask constraint (%s/%s)" , +multi_instance_string (mi, false, &gc), +print_in_addr_t (mi->context.c2.push_ifconfig_local,0 , &gc), + ifconfig_constraint_network, ifconfig_constraint_netmask); + } + +/* +* For routed tunnels, set up internal route to endpoint +* plus add all iroute routes. +*/ +if (TUNNEL_TYPE (mi->context.c1.tuntap) ==DEV_TYPE_TUN ) + { +if (mi->context.c2.push_ifconfig_defined) + { +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)); + } + +if (mi->context.c2.push_ifconfig_ipv6_defined) + { +multi_learn_in6_addr (m, mi, + mi->context.c2.push_ifconfig_ipv6_local, +-1 , true); +/* TODO: find out where addresses are "unlearned"!! */ +const char * ifconfig_local_ipv6 = +print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local,0 , &gc); +msg (D_MULTI_LOW ,"MULTI: primary virtual IPv6 for %s: %s" , +multi_instance_string (mi, false, &gc), + ifconfig_local_ipv6); + } + +/* add routes locally, pointing to new client, if +* --iroute options have been specified */ +multi_add_iroutes (m, mi); -#ifdef ENABLE_PLUGIN /* -* Call client-connect plug-in. +* iroutes represent subnets which are "owned" by a particular +* client. Therefore, do not actually push a route to a client +* if it matches one of the client's iroutes. */ +remove_iroutes_from_push_route_list (&mi->context.options); + } +else if (mi->context.options.iroutes) + { +msg (D_MULTI_ERRORS ,"MULTI: --iroute options rejected for %s -- iroute" +"only works with tun-style tunnels" , +multi_instance_string (mi, false, &gc)); + } -/* deprecated callback, use a file for passing back return info */ -if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT )) - { -struct argv argv =argv_new (); -const char *dc_file =platform_create_temp_file (mi->context.options.tmp_dir, -"cc" , &gc); +/* set our client's VPN endpoint for status reporting purposes */ + mi->reporting_addr = mi->context.c2.push_ifconfig_local; + mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local; -if (!dc_file) - { - cc_succeeded = false; - goto script_depr_failed; - } +/* set context-level authentication flag */ + mi->context.c2.context_auth =CAS_SUCCEEDED ; -argv_printf (&argv,"%s" , dc_file); -if (plugin_call (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT , &argv,NULL , mi->context.c2.es) !=OPENVPN_PLUGIN_FUNC_SUCCESS ) - { -msg (M_WARN ,"WARNING: client-connect plugin call failed" ); - cc_succeeded = false; - } -else - { -multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; - } +#ifdef ENABLE_ASYNC_PUSH +/* authentication complete, send push reply */ +if (mi->context.c2.push_request_received) + { +process_incoming_push_request (&mi->context); + } +#endif +gc_free (&gc); +} -if (!platform_unlink (dc_file)) - { -msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , - dc_file); - } -script_depr_failed: -argv_reset (&argv); - } +static void +multi_client_connect_early_setup (struct multi_context *m, +struct multi_instance *mi) +{ +ASSERT (mi->context.c1.tuntap); +/* +* lock down the common name and cert hashes so they can't change +* during future TLS renegotiations +*/ +tls_lock_common_name (mi->context.c2.tls_multi); +tls_lock_cert_hash_set (mi->context.c2.tls_multi); -/* V2 callback, use a plugin_return struct for passing back return info */ -if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 )) - { -struct plugin_return pr; +/* generate a msg() prefix for this client instance */ +generate_prefix (mi); -plugin_return_init (&pr); +/* delete instances of previous clients with same common-name */ +if (!mi->context.options.duplicate_cn) + { +multi_delete_dup (m, mi); + } -if (plugin_call (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 ,NULL , &pr, mi->context.c2.es) !=OPENVPN_PLUGIN_FUNC_SUCCESS ) - { -msg (M_WARN ,"WARNING: client-connect-v2 plugin call failed" ); - cc_succeeded = false; - } -else - { -multi_client_connect_post_plugin (m, mi, &pr, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; - } +/* reset pool handle to null */ + mi->vaddr_handle =-1 ; +} -plugin_return_free (&pr); - } -#endif /* ifdef ENABLE_PLUGIN */ +/** +* Try to source a dynamic config file from the +* --client-config-dir directory. +*/ +static void +multi_client_connect_source_ccd (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found) +{ +if (mi->context.options.client_config_dir) + { +struct gc_arena gc =gc_new (); +const char *ccd_file; -/* -* Run --client-connect script. -*/ -if (mi->context.options.client_connect_script && cc_succeeded) - { -struct argv argv =argv_new (); -const char *dc_file =NULL ; + ccd_file =platform_gen_path (mi->context.options.client_config_dir, +tls_common_name (mi->context.c2.tls_multi, + false), + &gc); -setenv_str (mi->context.c2.es,"script_type" ,"client-connect" ); +/* try common-name file */ +if (platform_test_file (ccd_file)) + { +options_server_import (&mi->context.options, + ccd_file, +D_IMPORT_ERRORS |M_OPTERR , +CLIENT_CONNECT_OPT_MASK , + option_types_found, + mi->context.c2.es); + } +else /* try default file */ + { + ccd_file =platform_gen_path (mi->context.options.client_config_dir, +CCD_DEFAULT , + &gc); - dc_file =platform_create_temp_file (mi->context.options.tmp_dir, -"cc" , &gc); -if (!dc_file) +if (platform_test_file (ccd_file)) { - cc_succeeded = false; - goto script_failed; +options_server_import (&mi->context.options, + ccd_file, +D_IMPORT_ERRORS |M_OPTERR , +CLIENT_CONNECT_OPT_MASK , + option_types_found, + mi->context.c2.es); } + } +gc_free (&gc); + } +} + +/* +* Called as soon as the SSL/TLS connection authenticates. +* +* Instance-specific directives to be processed: +* +* iroute start-ip end-ip +* ifconfig-push local remote-netmask +* push +*/ +static void +multi_connection_established (struct multi_context *m,struct multi_instance *mi) +{ +if (tls_authentication_status (mi->context.c2.tls_multi,0 ) + ==TLS_AUTHENTICATION_SUCCEEDED ) + { +unsigned int option_types_found =0 ; -argv_parse_cmd (&argv, mi->context.options.client_connect_script); -argv_printf_cat (&argv,"%s" , dc_file); +int cc_succeeded = true;/* client connect script status */ +int cc_succeeded_count =0 ; -if (openvpn_run_script (&argv, mi->context.c2.es,0 ,"--client-connect" )) - { -multi_client_connect_post (m, mi, dc_file, option_permissions_mask, &option_types_found); - ++cc_succeeded_count; - } -else - { - cc_succeeded = false; - } +multi_client_connect_early_setup (m, mi); -if (!platform_unlink (dc_file)) - { -msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , - dc_file); - } +multi_client_connect_source_ccd (m, mi, &option_types_found); -script_failed: -argv_reset (&argv); - } +/* +* Select a virtual address from either --ifconfig-push in +* --client-config-dir file or --ifconfig-pool. +*/ +multi_select_virtual_addr (m, mi); + +/* do --client-connect setenvs */ +multi_client_connect_setenv (m, mi); + +multi_client_connect_call_plugin_v1 (m, mi, &option_types_found, + &cc_succeeded, + &cc_succeeded_count); + +multi_client_connect_call_plugin_v2 (m, mi, &option_types_found, + &cc_succeeded, + &cc_succeeded_count);/* * Check for client-connect script left by management interface client */ + +if (cc_succeeded) + { +multi_client_connect_call_script (m, mi, &option_types_found, + &cc_succeeded, + &cc_succeeded_count); + + }#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; +multi_client_connect_mda (m, mi, &option_types_found); + cc_succeeded_count++; }#endif
@@ -1991,99 +2159,21 @@ script_failed:*/ if (mi->context.options.disable) { -msg (D_MULTI_ERRORS ,"MULTI: client has been rejected due to 'disable' directive" ); +msg (D_MULTI_ERRORS ,"MULTI: client has been rejected due to" +"'disable' directive" ); cc_succeeded = false; cc_succeeded_count =0 ; }if (cc_succeeded) { -/* -* Process sourced options. -*/ -do_deferred_options (&mi->context, option_types_found); - -/* -* make sure we got ifconfig settings from somewhere -*/ -if (!mi->context.c2.push_ifconfig_defined) - { -msg (D_MULTI_ERRORS ,"MULTI: no dynamic or static remote --ifconfig address is available for %s" , -multi_instance_string (mi, false, &gc)); - } - -/* -* make sure that ifconfig settings comply with constraints -*/ -if (!ifconfig_push_constraint_satisfied (&mi->context)) - { -/* JYFIXME -- this should cause the connection to fail */ -msg (D_MULTI_ERRORS ,"MULTI ERROR: primary virtual IP for %s (%s) violates tunnel network/netmask constraint (%s/%s)" , -multi_instance_string (mi, false, &gc), -print_in_addr_t (mi->context.c2.push_ifconfig_local,0 , &gc), -print_in_addr_t (mi->context.options.push_ifconfig_constraint_network,0 , &gc), -print_in_addr_t (mi->context.options.push_ifconfig_constraint_netmask,0 , &gc)); - } - -/* -* For routed tunnels, set up internal route to endpoint -* plus add all iroute routes. -*/ -if (TUNNEL_TYPE (mi->context.c1.tuntap) ==DEV_TYPE_TUN ) - { -if (mi->context.c2.push_ifconfig_defined) - { -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)); - } - -if (mi->context.c2.push_ifconfig_ipv6_defined) - { -multi_learn_in6_addr (m, mi, mi->context.c2.push_ifconfig_ipv6_local,-1 , true); -/* TODO: find out where addresses are "unlearned"!! */ -msg (D_MULTI_LOW ,"MULTI: primary virtual IPv6 for %s: %s" , -multi_instance_string (mi, false, &gc), -print_in6_addr (mi->context.c2.push_ifconfig_ipv6_local,0 , &gc)); - } - -/* add routes locally, pointing to new client, if -* --iroute options have been specified */ -multi_add_iroutes (m, mi); - -/* -* iroutes represent subnets which are "owned" by a particular -* client. Therefore, do not actually push a route to a client -* if it matches one of the client's iroutes. -*/ -remove_iroutes_from_push_route_list (&mi->context.options); - } -else if (mi->context.options.iroutes) - { -msg (D_MULTI_ERRORS ,"MULTI: --iroute options rejected for %s -- iroute only works with tun-style tunnels" , -multi_instance_string (mi, false, &gc)); - } - -/* set our client's VPN endpoint for status reporting purposes */ - mi->reporting_addr = mi->context.c2.push_ifconfig_local; - mi->reporting_addr_ipv6 = mi->context.c2.push_ifconfig_ipv6_local; - -/* 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 +multi_client_connect_late_setup (m, mi, option_types_found); }else {/* set context-level authentication flag */ - mi->context.c2.context_auth = cc_succeeded_count ?CAS_PARTIAL :CAS_FAILED ; + mi->context.c2.context_auth = + cc_succeeded_count ?CAS_PARTIAL :CAS_FAILED ; }/* set flag so we don't get called again */
@@ -2097,11 +2187,11 @@ script_failed:#ifdef MANAGEMENT_DEF_AUTH if (management) { -management_connection_established (management, &mi->context.c2.mda_context, mi->context.c2.es); +management_connection_established + (management, &mi->context.c2.mda_context, mi->context.c2.es); }#endif -gc_free (&gc); }/*
src/openvpn/multi.h +3 -1
@@ -628,7 +628,9 @@ multi_process_outgoing_tun(struct multi_context *m, const unsigned int mpp_flagsreturn ret; } - +#define CLIENT_CONNECT_OPT_MASK (OPT_P_INSTANCE | OPT_P_INHERIT | \ + OPT_P_PUSH | OPT_P_TIMER | OPT_P_CONFIG | \ + OPT_P_ECHO | OPT_P_COMP | OPT_P_SOCKFLAGS)static inline bool multi_process_outgoing_link_dowork (struct multi_context *m,struct multi_instance *mi,const unsigned int mpp_flags)
Signature · 1 line
2.19.1
PATCH v3 2/13 client-connect: Refactor multi_client_connect_source_ccd
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
Refactor multi_client_connect_source_ccd(), so that options_server_import() (or the success path in general) is only entered in one place within the function.
--- src/openvpn/multi.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-)
src/openvpn/multi.c +15 -16
@@ -2064,31 +2064,30 @@ multi_client_connect_source_ccd(struct multi_context *m, &gc);/* try common-name file */ -if (platform_test_file (ccd_file)) +if (!platform_test_file (ccd_file)) { -options_server_import (&mi->context.options, - ccd_file, -D_IMPORT_ERRORS |M_OPTERR , -CLIENT_CONNECT_OPT_MASK , - option_types_found, - mi->context.c2.es); + ccd_file =NULL ; } -else /* try default file */ +/* try default file */ { ccd_file =platform_gen_path (mi->context.options.client_config_dir,CCD_DEFAULT , &gc); - -if (platform_test_file (ccd_file)) +if (!platform_test_file (ccd_file)) { -options_server_import (&mi->context.options, - ccd_file, -D_IMPORT_ERRORS |M_OPTERR , -CLIENT_CONNECT_OPT_MASK , - option_types_found, - mi->context.c2.es); + ccd_file =NULL ; } } + +if (ccd_file) + { +options_server_import (&mi->context.options, + ccd_file, +D_IMPORT_ERRORS |M_OPTERR , +CLIENT_CONNECT_OPT_MASK , + option_types_found, + mi->context.c2.es); + }gc_free (&gc); } }
Signature · 1 line
2.19.1
PATCH v3 3/13 client-connect: Move multi_client_connect_setenv into early_setup
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
This patch moves multi_client_connect_setenv into multi_client_connect_early_setup and makes sure that every client-connect handling function updates the virtual address selection.
Background: This unifies how the client-connect handling functions work.
--- src/openvpn/multi.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-)
src/openvpn/multi.c +13 -9
@@ -2042,6 +2042,12 @@ multi_client_connect_early_setup(struct multi_context *m,/* reset pool handle to null */ mi->vaddr_handle =-1 ; + +/* do --client-connect setenvs */ +multi_select_virtual_addr (m, mi); + +multi_client_connect_setenv (m, mi); + }/**
@@ -2087,6 +2093,13 @@ multi_client_connect_source_ccd(struct multi_context *m,CLIENT_CONNECT_OPT_MASK , option_types_found, mi->context.c2.es); +/* +* Select a virtual address from either --ifconfig-push in +* --client-config-dir file or --ifconfig-pool. +*/ +multi_select_virtual_addr (m, mi); + +multi_client_connect_setenv (m, mi); }gc_free (&gc); }
@@ -2116,15 +2129,6 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)multi_client_connect_source_ccd (m, mi, &option_types_found); -/* -* Select a virtual address from either --ifconfig-push in -* --client-config-dir file or --ifconfig-pool. -*/ -multi_select_virtual_addr (m, mi); - -/* do --client-connect setenvs */ -multi_client_connect_setenv (m, mi); -multi_client_connect_call_plugin_v1 (m, mi, &option_types_found, &cc_succeeded, &cc_succeeded_count);
Signature · 1 line
2.19.1
PATCH v3 4/13 client-connect: Refactor to use return values instead of modifying a passed-in flag
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
This patch changes the way the client-connect helper functions communicate with the main function. Instead of updating cc_succeeded and cc_succeeded_count, they now return either CC_RET_SUCCEEDED, CC_RET_FAILED or CC_RET_SKIPPED.
In addition, the client-connect helpers are now called in completely identical ways. This is in preparation of handling the helpers as simple call-backs.
--- src/openvpn/multi.c | 138 +++++++++++++++++++++++++++----------------- src/openvpn/multi.h | 10 ++++ 2 files changed, 94 insertions(+), 54 deletions(-)
src/openvpn/multi.c +84 -54
@@ -1706,20 +1706,21 @@ multi_client_connect_post_plugin(struct multi_context *m,#endif /* ifdef ENABLE_PLUGIN */ -#ifdef MANAGEMENT_DEF_AUTH +/* * Called to load management-derived client-connect config */ -static void +enum client_connect_return multi_client_connect_mda (struct multi_context *m,struct multi_instance *mi,unsigned int *option_types_found) { +enum client_connect_return ret =CC_RET_SKIPPED ; +#ifdef MANAGEMENT_DEF_AUTH if (mi->cc_config) {struct buffer_entry *be; -for (be = mi->cc_config->head; be !=NULL ; be = be->next) {const char *opt =BSTR (&be->buf);
@@ -1739,10 +1740,12 @@ multi_client_connect_mda(struct multi_context *m,*/ multi_select_virtual_addr (m, mi);multi_set_virtual_addr_env (m, mi); - } -} + ret =CC_RET_SUCCEEDED ; + }#endif /* ifdef MANAGEMENT_DEF_AUTH */ +return ret; +} static void multi_client_connect_setenv(struct multi_context *m,
@@ -1769,19 +1772,16 @@ multi_client_connect_setenv(struct multi_context *m,gc_free (&gc); } -static void +static enum client_connect_return multi_client_connect_call_plugin_v1 (struct multi_context *m,struct multi_instance *mi, -unsigned int *option_types_found, -int *cc_succeeded, -int *cc_succeeded_count) +unsigned int *option_types_found) { +enum client_connect_return ret =CC_RET_SKIPPED ;#ifdef ENABLE_PLUGIN -ASSERT (m); -ASSERT (mi); -ASSERT (option_types_found); -ASSERT (cc_succeeded); -ASSERT (cc_succeeded_count); +ASSERT (m); +ASSERT (mi); +ASSERT (option_types_found);/* deprecated callback, use a file for passing back return info */ if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT ))
@@ -1793,7 +1793,7 @@ multi_client_connect_call_plugin_v1(struct multi_context *m,if (!dc_file) { - cc_succeeded = false; + ret =CC_RET_FAILED ; goto cleanup; }
@@ -1803,12 +1803,12 @@ multi_client_connect_call_plugin_v1(struct multi_context *m, != OPENVPN_PLUGIN_FUNC_SUCCESS) { msg(M_WARN, "WARNING: client-connect plugin call failed"); - *cc_succeeded = false; + ret=CC_RET_FAILED; } else {multi_client_connect_post (m, mi, dc_file, option_types_found); - (*cc_succeeded_count)++; + ret =CC_RET_SUCCEEDED ; }if (!platform_unlink (dc_file))
@@ -1822,21 +1822,19 @@ multi_client_connect_call_plugin_v1(struct multi_context *m, gc_free(&gc); }#endif /* ifdef ENABLE_PLUGIN */ +return ret; } -static void +static enum client_connect_return multi_client_connect_call_plugin_v2 (struct multi_context *m,struct multi_instance *mi, -unsigned int *option_types_found, -int *cc_succeeded, -int *cc_succeeded_count) +unsigned int *option_types_found) { +enum client_connect_return ret =CC_RET_SKIPPED ;#ifdef ENABLE_PLUGIN -ASSERT (m); -ASSERT (mi); -ASSERT (option_types_found); -ASSERT (cc_succeeded); -ASSERT (cc_succeeded_count); +ASSERT (m); +ASSERT (mi); +ASSERT (option_types_found);/* V2 callback, use a plugin_return struct for passing back return info */ if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 ))
@@ -1850,17 +1848,18 @@ multi_client_connect_call_plugin_v2(struct multi_context *m, != OPENVPN_PLUGIN_FUNC_SUCCESS) { msg(M_WARN, "WARNING: client-connect-v2 plugin call failed"); - *cc_succeeded = false; + ret = CC_RET_FAILED; } else {multi_client_connect_post_plugin (m, mi, &pr, option_types_found); - (*cc_succeeded_count)++; + ret =CC_RET_SUCCEEDED ; } plugin_return_free(&pr); }#endif /* ifdef ENABLE_PLUGIN */ +return ret; }
@@ -1868,13 +1867,12 @@ multi_client_connect_call_plugin_v2(struct multi_context *m,/** * Runs the --client-connect script if one is defined. */ -static void +static enum client_connect_return multi_client_connect_call_script (struct multi_context *m,struct multi_instance *mi, -unsigned int *option_types_found, -int *cc_succeeded, -int *cc_succeeded_count) +unsigned int *option_types_found) { +enum client_connect_return ret =CC_RET_SKIPPED ;if (mi->context.options.client_connect_script) {struct argv argv =argv_new ();
@@ -1887,7 +1885,7 @@ multi_client_connect_call_script(struct multi_context *m, "cc", &gc);if (!dc_file) { - *cc_succeeded = false; + ret =CC_RET_FAILED ; goto cleanup; }
@@ -1897,11 +1895,11 @@ multi_client_connect_call_script(struct multi_context *m,if (openvpn_run_script (&argv, mi->context.c2.es,0 ,"--client-connect" )) {multi_client_connect_post (m, mi, dc_file, option_types_found); - (*cc_succeeded_count)++; + ret =CC_RET_SUCCEEDED ; }else { - *cc_succeeded = false; + ret =CC_RET_FAILED ; }if (!platform_unlink (dc_file))
@@ -1913,6 +1911,7 @@ multi_client_connect_call_script(struct multi_context *m,argv_reset (&argv);gc_free (&gc); } +return ret; } static void
@@ -2047,18 +2046,18 @@ multi_client_connect_early_setup(struct multi_context *m,multi_select_virtual_addr (m, mi);multi_client_connect_setenv (m, mi); - }/** * Try to source a dynamic config file from the * --client-config-dir directory. */ -static void +enum client_connect_return multi_client_connect_source_ccd (struct multi_context *m,struct multi_instance *mi,unsigned int *option_types_found) { +enum client_connect_return ret =CC_RET_SKIPPED ;if (mi->context.options.client_config_dir) {struct gc_arena gc =gc_new ();
@@ -2100,9 +2099,35 @@ multi_client_connect_source_ccd(struct multi_context *m,multi_select_virtual_addr (m, mi);multi_client_connect_setenv (m, mi); + + ret =CC_RET_SUCCEEDED ; }gc_free (&gc); } +return ret; +} + +static inline bool +cc_check_return (int * cc_succeeded_count, +enum client_connect_return ret) +{ +if (ret ==CC_RET_SUCCEEDED ) + { + (*cc_succeeded_count)++; +return true; + } +else if (ret ==CC_RET_FAILED ) + { +return false; + } +else if (ret ==CC_RET_SKIPPED ) + { +return true; + } +else + { +ASSERT (0 ); + } }/*
@@ -2124,37 +2149,42 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)int cc_succeeded = true;/* client connect script status */ int cc_succeeded_count =0 ; +enum client_connect_return ret;multi_client_connect_early_setup (m, mi); -multi_client_connect_source_ccd (m, mi, &option_types_found); + ret =multi_client_connect_source_ccd (m, mi, &option_types_found); + cc_succeeded =cc_check_return (&cc_succeeded_count, ret); -multi_client_connect_call_plugin_v1 (m, mi, &option_types_found, - &cc_succeeded, - &cc_succeeded_count); +if (cc_succeeded) + { + ret =multi_client_connect_call_plugin_v1 (m, mi, + &option_types_found); + cc_succeeded =cc_check_return (&cc_succeeded_count, ret); + } + +if (cc_succeeded) + { + ret =multi_client_connect_call_plugin_v2 (m, mi, + &option_types_found); + cc_succeeded =cc_check_return (&cc_succeeded_count, ret); + } -multi_client_connect_call_plugin_v2 (m, mi, &option_types_found, - &cc_succeeded, - &cc_succeeded_count);/* * Check for client-connect script left by management interface client */ -if (cc_succeeded) { -multi_client_connect_call_script (m, mi, &option_types_found, - &cc_succeeded, - &cc_succeeded_count); - + ret =multi_client_connect_call_script (m, mi, &option_types_found); + cc_succeeded =cc_check_return (&cc_succeeded_count, ret); } -#ifdef MANAGEMENT_DEF_AUTH -if (cc_succeeded && mi->cc_config) + +if (cc_succeeded) { -multi_client_connect_mda (m, mi, &option_types_found); - cc_succeeded_count++; + ret =multi_client_connect_mda (m, mi, &option_types_found); + cc_succeeded =cc_check_return (&cc_succeeded_count, ret); } -#endif /* * Check for "disable" directive in client-config-dir file
src/openvpn/multi.h +10 -0
@@ -191,6 +191,16 @@ struct multi_context {struct deferred_signal_schedule_entry deferred_shutdown_signal; }; +/** +* Return values used by the client connect call-back functions. +*/ +enum client_connect_return +{ +CC_RET_FAILED , +CC_RET_SUCCEEDED , +CC_RET_SKIPPED +}; +/* * Host route */
Signature · 1 line
2.19.1
PATCH v3 5/13 client-connect: Refactor client-connect handling to calling a bunch of hooks in a loop
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
This patch changes the calling of the client-connect functions into an array of hooks and a block of code that calls them in a loop.
--- src/openvpn/multi.c | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-)
src/openvpn/multi.c +16 -29
@@ -2145,6 +2145,20 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)if (tls_authentication_status (mi->context.c2.tls_multi,0 ) ==TLS_AUTHENTICATION_SUCCEEDED ) { +typedef enum client_connect_return + (*multi_client_connect_handler ) + (struct multi_context *m,struct multi_instance *mi, +unsigned int *option_types_found); + +multi_client_connect_handler handlers[] = { + multi_client_connect_source_ccd, + multi_client_connect_call_plugin_v1, + multi_client_connect_call_plugin_v2, + multi_client_connect_call_script, + multi_client_connect_mda, +NULL + }; +unsigned int option_types_found =0 ;int cc_succeeded = true;/* client connect script status */
@@ -2153,36 +2167,9 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)multi_client_connect_early_setup (m, mi); - ret =multi_client_connect_source_ccd (m, mi, &option_types_found); - cc_succeeded =cc_check_return (&cc_succeeded_count, ret); - -if (cc_succeeded) - { - ret =multi_client_connect_call_plugin_v1 (m, mi, - &option_types_found); - cc_succeeded =cc_check_return (&cc_succeeded_count, ret); - } - -if (cc_succeeded) - { - ret =multi_client_connect_call_plugin_v2 (m, mi, - &option_types_found); - cc_succeeded =cc_check_return (&cc_succeeded_count, ret); - } - - -/* -* Check for client-connect script left by management interface client -*/ -if (cc_succeeded) - { - ret =multi_client_connect_call_script (m, mi, &option_types_found); - cc_succeeded =cc_check_return (&cc_succeeded_count, ret); - } - -if (cc_succeeded) +for (int i =0 ;cc_succeeded && handlers[i];i++) { - ret =multi_client_connect_mda (m, mi, &option_types_found); + ret = handlers[i](m, mi, &option_types_found); cc_succeeded =cc_check_return (&cc_succeeded_count, ret); }
Signature · 1 line
2.19.1
PATCH v3 6/13 client-connect: Change connection_established_flag from bool to enum
In reply to Arne SchwabeThis prepares from the yes/no logic to a tristate logic with defered being the third state. This deviates from Fabian's original patch that that used a pointer being NULL or non NULL as implicit third state.
--- src/openvpn/multi.c | 14 ++++++++------ src/openvpn/multi.h | 8 +++++++- 2 files changed, 15 insertions(+), 7 deletions(-)
src/openvpn/multi.c +8 -6
@@ -574,7 +574,8 @@ static void multi_client_disconnect_script(struct multi_context *m, struct multi_instance *mi) { -if ((mi->context.c2.context_auth ==CAS_SUCCEEDED && mi->connection_established_flag) +if ((mi->context.c2.context_auth ==CAS_SUCCEEDED && + mi->client_connect_status ==CC_STATUS_ESTABLISHED ) || mi->context.c2.context_auth ==CAS_PARTIAL ) {multi_client_disconnect_setenv (m, mi);
@@ -2052,7 +2053,7 @@ multi_client_connect_early_setup(struct multi_context *m,* Try to source a dynamic config file from the * --client-config-dir directory. */ -enum client_connect_return +static enum client_connect_return multi_client_connect_source_ccd (struct multi_context *m,struct multi_instance *mi,unsigned int *option_types_found)
@@ -2197,7 +2198,7 @@ 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; + mi->client_connect_status =CC_STATUS_ESTABLISHED ;/* increment number of current authenticated clients */ ++m->n_clients;
@@ -2480,7 +2481,8 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns {/* 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)) +if (mi->client_connect_status !=CC_STATUS_ESTABLISHED + &&CONNECTION_ESTABLISHED (&mi->context)) {multi_connection_established (m, mi); }
@@ -3386,7 +3388,7 @@ management_client_auth(void *arg, {if (auth) { -if (!mi->connection_established_flag) +if (mi->client_connect_status ==CC_STATUS_NOT_ESTABLISHED ) {set_cc_config (mi, cc_config); cc_config_owned = false;
@@ -3398,7 +3400,7 @@ management_client_auth(void *arg, {msg (D_MULTI_LOW ,"MULTI: connection rejected: %s, CLI:%s" , reason,np (client_reason)); } -if (mi->connection_established_flag) +if (mi->client_connect_status ==CC_STATUS_ESTABLISHED ) {send_auth_failed (&mi->context, client_reason);/* mid-session reauth failed */ multi_schedule_context_wakeup (m, mi);
src/openvpn/multi.h +7 -1
@@ -63,6 +63,12 @@ struct deferred_signal_schedule_entrystruct timeval wakeup; }; +enum client_connect_status +{ +CC_STATUS_NOT_ESTABLISHED , +CC_STATUS_ESTABLISHED +}; +/** * Server-mode state structure for one single VPN tunnel. *
@@ -105,7 +111,7 @@ struct multi_instance {bool did_cid_hash;struct buffer_list *cc_config;#endif -bool connection_established_flag; +enum client_connect_status client_connect_status;bool did_iroutes;int n_clients_delta;/* added to multi_context.n_clients when instance is closed */
Signature · 1 line
2.19.1
PATCH v3 7/13 client-connect: Add CC_RET_DEFERRED and cope with deferred client-connect
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
This patch moves the state, that was previously tracked within the multi_connection_established() function, into struct client_connect_state. The multi_connection_established() function can now be exited and re-entered as many times as necessary - without losing the client-connect handling state.
The patch also adds the new return value CC_RET_DEFERRED which indicates that the handler couldn't complete immediately, and needs to be called later. At that point multi_connection_established() will exit without indicating completion.
Each client-connect handler now has an (optional) additional call-back: The call-back for handling the deferred case. If the main call-back returns CC_RET_DEFERRED, the next call to the handler will be through the deferred call-back.
Patch V3: Use a static struct in multi_instance instead of using
malloc/free and use two states (deffered with and without result) instead of one to eliminate the counter that was only tested for > 0.
--- src/openvpn/multi.c | 158 +++++++++++++++++++++++++++++++++----------- src/openvpn/multi.h | 18 ++++- 2 files changed, 136 insertions(+), 40 deletions(-)
src/openvpn/multi.c +119 -39
@@ -2108,28 +2108,51 @@ multi_client_connect_source_ccd(struct multi_context *m,return ret; } -static inline bool -cc_check_return (int * cc_succeeded_count, -enum client_connect_return ret) +typedef enum client_connect_return (*client_connect_handler ) + (struct multi_context *m,struct multi_instance *mi, +unsigned int *option_types_found); + +struct client_connect_handlers +{ +client_connect_handler main; +client_connect_handler deferred; +}; + +static enum client_connect_return +multi_client_connect_fail (struct multi_context *m,struct multi_instance *mi, +unsigned int *option_types_found) { -if (ret ==CC_RET_SUCCEEDED ) +/* Called null call-back. This should never happen. */ +return CC_RET_FAILED ; +} + +static const struct client_connect_handlers client_connect_handlers[] = { { - (*cc_succeeded_count)++; -return true; - } -else if (ret ==CC_RET_FAILED ) + .main = multi_client_connect_source_ccd, + .deferred = multi_client_connect_fail + }, { -return false; - } -else if (ret ==CC_RET_SKIPPED ) + .main = multi_client_connect_call_plugin_v1, + .deferred = multi_client_connect_fail + }, { -return true; - } -else + .main = multi_client_connect_call_plugin_v2, + .deferred = multi_client_connect_fail + }, + { + .main = multi_client_connect_call_script, + .deferred = multi_client_connect_fail + }, { -ASSERT (0 ); + .main = multi_client_connect_mda, + .deferred = multi_client_connect_fail + }, + { + .main =NULL , + .deferred =NULL +/* End of list sentinel. */ } -} +};/* * Called as soon as the SSL/TLS connection authenticates.
@@ -2146,32 +2169,86 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)if (tls_authentication_status (mi->context.c2.tls_multi,0 ) ==TLS_AUTHENTICATION_SUCCEEDED ) { -typedef enum client_connect_return - (*multi_client_connect_handler ) - (struct multi_context *m,struct multi_instance *mi, -unsigned int *option_types_found); +bool from_deferred; -multi_client_connect_handler handlers[] = { - multi_client_connect_source_ccd, - multi_client_connect_call_plugin_v1, - multi_client_connect_call_plugin_v2, - multi_client_connect_call_script, - multi_client_connect_mda, -NULL - }; +enum client_connect_return ret; -unsigned int option_types_found =0 ; +struct client_connect_defer_state * defer_state = + &(mi->client_connect_defer_state); -int cc_succeeded = true;/* client connect script status */ -int cc_succeeded_count =0 ; -enum client_connect_return ret; +/* We are called for the first time */ +if (mi->client_connect_status ==CC_STATUS_NOT_ESTABLISHED ) + { + defer_state->cur_handler_index =0 ; + defer_state->option_types_found =0 ; +/* Initially we have no handler that has returned a result */ + mi->client_connect_status =CC_STATUS_DEFERRED_NO_RESULT ; + from_deferred = false; + } +else + { + from_deferred = true; + }multi_client_connect_early_setup (m, mi); -for (int i =0 ;cc_succeeded && handlers[i];i++) +bool cc_succeeded=true; + +while (cc_succeeded && + client_connect_handlers[defer_state->cur_handler_index] + .main !=NULL ) { - ret = handlers[i](m, mi, &option_types_found); - cc_succeeded =cc_check_return (&cc_succeeded_count, ret); +client_connect_handler handler; +if (from_deferred) + { + handler = client_connect_handlers + [defer_state->cur_handler_index].deferred; + from_deferred = false; + } +else + { + handler = client_connect_handlers + [defer_state->cur_handler_index].main; + } + + ret =handler (m, mi, &(defer_state->option_types_found)); +if (ret ==CC_RET_SUCCEEDED ) + { +/* +* Remember that we already had at least one handler +* returning a result should go to into defered state +*/ + mi->client_connect_status =CC_STATUS_DEFERRED_RESULT ; + } +else if (ret ==CC_RET_SKIPPED ) + { +/* +* Move on with the next handler without modifying any +* other state +*/ + } +else if (ret ==CC_RET_DEFERRED ) + { +/* +* we already set client_connect_status to DEFERRED_RESULT or +* DEFERRED_NO_RESULT and increased index. We just return +* from the function as having client_connect_status +*/ +return ; + } +else if (ret ==CC_RET_FAILED ) + { +/* +* One handler failed. We abort the chain and set the final +* result to failed +*/ + cc_succeeded = false; + } +else + { +ASSERT (0 ); + } + (defer_state->cur_handler_index)++; }/*
@@ -2183,21 +2260,24 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)msg (D_MULTI_ERRORS ,"MULTI: client has been rejected due to" "'disable' directive" ); cc_succeeded = false; - cc_succeeded_count =0 ; }if (cc_succeeded) { -multi_client_connect_late_setup (m, mi, option_types_found); +multi_client_connect_late_setup (m, mi, + mi->client_connect_defer_state. + option_types_found); }else { +bool at_least_one_cc_succeeded = + (mi->client_connect_status ==CC_STATUS_DEFERRED_RESULT );/* set context-level authentication flag */ mi->context.c2.context_auth = - cc_succeeded_count ?CAS_PARTIAL :CAS_FAILED ; + at_least_one_cc_succeeded ?CAS_PARTIAL :CAS_FAILED ; } -/* set flag so we don't get called again */ +/* set flag so we do not get called again */ mi->client_connect_status =CC_STATUS_ESTABLISHED ;/* increment number of current authenticated clients */
src/openvpn/multi.h +17 -1
@@ -63,9 +63,24 @@ struct deferred_signal_schedule_entrystruct timeval wakeup; }; +/** +* Detached client connection state. This is the state that is tracked while +* the client connect hooks are executed. +*/ +struct client_connect_defer_state +{ +/* Index of currently executed handler. */ +int cur_handler_index; +/* Remember which option classes where processed for delayed option +handling. */ +unsigned int option_types_found; +}; +enum client_connect_status {CC_STATUS_NOT_ESTABLISHED , +CC_STATUS_DEFERRED_NO_RESULT , +CC_STATUS_DEFERRED_RESULT ,CC_STATUS_ESTABLISHED };
@@ -117,7 +132,7 @@ struct multi_instance {struct context context;/**< The context structure storing state * for this VPN tunnel. */ - +struct client_connect_defer_state client_connect_defer_state;#ifdef ENABLE_ASYNC_PUSH int inotify_watch;/* watch descriptor for acf */ #endif
@@ -204,6 +219,7 @@ enum client_connect_return {CC_RET_FAILED ,CC_RET_SUCCEEDED , +CC_RET_DEFERRED , CC_RET_SKIPPED };
Signature · 1 line
2.19.1
PATCH v3 8/13 client-connect: Add deferred support to the client-connect script handler
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
This patch introduces the concept of a return value file for the client-connect handlers. (This is very similar to the auth value file used during deferred authentication.) The file name is stored in the client_connect_state struct.
In addition, the patch also allows the storage of the client config file name in struct client_connect_state.
Both changes are used by the client-connect script handler to support deferred client-connection handling. The deferred return value file (deferred_ret_file) is passed to the actual script via the environment. If the script succeeds and writes the value for deferral into the deferred_ret_file, the handler knows to indicate deferral. Later on, the deferred handler checks whether the value of the deferred_ret_file has been updated to success or failure.
--- src/openvpn/multi.c | 218 +++++++++++++++++++++++++++++++++++++++++--- src/openvpn/multi.h | 12 +++ 2 files changed, 215 insertions(+), 15 deletions(-)
src/openvpn/multi.c +203 -15
@@ -1773,6 +1773,156 @@ multi_client_connect_setenv(struct multi_context *m,gc_free (&gc); } +/** +* Delete the temporary file for the return value of client connect +* It also removes it from it from client_connect_defer_state and +* environment +*/ +static void +ccs_delete_deferred_ret_file (struct multi_instance *mi) +{ +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +if (ccs->deferred_ret_file) + { +setenv_del (mi->context.c2.es,"client_connect_deferred_file" ); +if (!platform_unlink (ccs->deferred_ret_file)) +msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , + ccs->deferred_ret_file); +free (ccs->deferred_ret_file); + ccs->deferred_ret_file =NULL ; + } +} + +/** +* Create a temporary file for the return value of client connect +* and puts it into the client_connect_defer_state and environment +* as "client_connect_deferred_file" +* +* @return boolean value if creation was successfull +*/ +static bool +ccs_gen_deferred_ret_file (struct multi_instance *mi) +{ +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +struct gc_arena gc =gc_new (); +const char *fn; + +if (ccs->deferred_ret_file) +ccs_delete_deferred_ret_file (mi); + + fn =platform_create_temp_file (mi->context.options.tmp_dir,"ccr" , &gc); +if (!fn) + { +gc_free (&gc); +return false; + } + ccs->deferred_ret_file =string_alloc (fn,NULL ); + +setenv_str (mi->context.c2.es,"client_connect_deferred_file" , + ccs->deferred_ret_file); + +gc_free (&gc); +return true; +} + +/** +* Tests whether the deferred return value file exists and returns the +* contained return value. +* +* @return CC_RET_SKIPPED if the file does not exist or is empty. +* CC_RET_DEFERRED, CC_RET_SUCCEEDED or CC_RET_FAILED depending on +* the value stored in the file. +*/ +static enum client_connect_return +ccs_test_deferred_ret_file (struct multi_instance *mi) +{ +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +enum client_connect_return ret =CC_RET_SKIPPED ; +FILE *fp =fopen (ccs->deferred_ret_file,"r" ); +if (fp) + { +const int c =fgetc (fp); +switch (c) + { +case '0' : + ret =CC_RET_FAILED ; +break ; +case '1' : + ret =CC_RET_SUCCEEDED ; +break ; +case '2' : + ret =CC_RET_DEFERRED ; +break ; +case EOF : +if (feof (fp)) + { + ret =CC_RET_SKIPPED ; +break ; + } +/* Not EOF, but other error fall through to error state */ +default : +/* We received an unknown/unexpected value. Assume failure. */ +msg (M_WARN ,"WARNING: Unknown/unexcepted value in deferred" +"client-connect resultfile" ); + ret =CC_RET_FAILED ; + } +fclose (fp); + } +return ret; +} + +/** +* Deletes the temporary file for the config directives of the client connect +* script and removes it into the client_connect_defer_state and environment +* +*/ +static void +ccs_delete_config_file (struct multi_instance *mi) +{ +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +if (ccs->config_file) + { +setenv_del (mi->context.c2.es,"client_connect_config_file" ); +if (!platform_unlink (ccs->config_file)) +msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , + ccs->config_file); +free (ccs->config_file); + ccs->config_file =NULL ; + } +} + +/** +* Create a temporary file for the config directives of the client connect +* script and puts it into the client_connect_defer_state and environment +* as "client_connect_config_file" +* +* @return boolean value if creation was successfull +*/ +static bool +ccs_gen_config_file (struct multi_instance *mi) +{ +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +struct gc_arena gc =gc_new (); +const char *fn; + +if (ccs->config_file) +ccs_delete_config_file (mi); + + fn =platform_create_temp_file (mi->context.options.tmp_dir,"cc" , &gc); +if (!fn) + { +gc_free (&gc); +return false; + } + ccs->config_file =string_alloc (fn,NULL ); + +setenv_str (mi->context.c2.es,"client_connect_config_file" , + ccs->config_file); + +gc_free (&gc); +return true; +} + static enum client_connect_return multi_client_connect_call_plugin_v1(struct multi_context *m, struct multi_instance *mi,
@@ -1863,8 +2013,6 @@ multi_client_connect_call_plugin_v2(struct multi_context *m,return ret; } - -/** * Runs the --client-connect script if one is defined. */
@@ -1874,47 +2022,87 @@ multi_client_connect_call_script(struct multi_context *m, unsigned int *option_types_found) {enum client_connect_return ret =CC_RET_SKIPPED ; +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +if (mi->context.options.client_connect_script) {struct argv argv =argv_new ();struct gc_arena gc =gc_new (); -const char *dc_file =NULL ;setenv_str (mi->context.c2.es,"script_type" ,"client-connect" ); - dc_file =platform_create_temp_file (mi->context.options.tmp_dir, -"cc" , &gc); -if (!dc_file) +if (!ccs_gen_config_file (mi) || + !ccs_gen_deferred_ret_file (mi)) { ret =CC_RET_FAILED ; goto cleanup; }argv_parse_cmd (&argv, mi->context.options.client_connect_script); -argv_printf_cat (&argv,"%s" , dc_file); +argv_printf_cat (&argv,"%s" , ccs->config_file);if (openvpn_run_script (&argv, mi->context.c2.es,0 ,"--client-connect" )) { -multi_client_connect_post (m, mi, dc_file, option_types_found); - ret =CC_RET_SUCCEEDED ; +if (ccs_test_deferred_ret_file (mi) ==CC_RET_DEFERRED ) + { + ret =CC_RET_DEFERRED ; + } +else + { +multi_client_connect_post (m, mi, ccs->config_file, + option_types_found); + ret =CC_RET_SUCCEEDED ; + } }else { ret =CC_RET_FAILED ; } - -if (!platform_unlink (dc_file)) + cleanup: +if (ret !=CC_RET_DEFERRED ) { -msg (D_MULTI_ERRORS ,"MULTI: problem deleting temporary file: %s" , - dc_file); +ccs_delete_config_file (mi); +ccs_delete_deferred_ret_file (mi); } - cleanup:argv_reset (&argv);gc_free (&gc); }return ret; } +static enum client_connect_return +multi_client_handle_deferred (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found) +{ +ASSERT (mi); +ASSERT (option_types_found); +struct client_connect_defer_state *ccs = &(mi->client_connect_defer_state); +enum client_connect_return ret =CC_RET_SKIPPED ; + + ret =ccs_test_deferred_ret_file (mi); + +if (ret ==CC_RET_SKIPPED ) +/* +* Skipped and deferred are equivalent in this context. +* skipped means that the called program has not yet +* written a return status implicitly needing more time +* while deferred is the explicit notifcation that it +* needs more time +*/ + ret =CC_RET_DEFERRED ; + +if (ret !=CC_RET_DEFERRED ) + { +ccs_delete_deferred_ret_file (mi); +multi_client_connect_post (m, mi, ccs->config_file, + option_types_found); +ccs_delete_config_file (mi); + } +return ret; +} + + static void multi_client_connect_late_setup(struct multi_context *m, struct multi_instance *mi,
@@ -2141,7 +2329,7 @@ static const struct client_connect_handlers client_connect_handlers[] = { }, { .main = multi_client_connect_call_script, - .deferred = multi_client_connect_fail + .deferred = multi_client_handle_deferred }, { .main = multi_client_connect_mda,
src/openvpn/multi.h +12 -0
@@ -74,6 +74,18 @@ struct client_connect_defer_state/* Remember which option classes where processed for delayed option handling. */ unsigned int option_types_found; + +/** +*The temporrary file name that contains the return status of the +* client-connect script if it exits with defer as status +*/ +char *deferred_ret_file; + +/** +* The temporary file name that contains the config directives +* returned by the client-connect script +*/ +char *config_file; };enum client_connect_status
Signature · 1 line
2.19.1
PATCH v3 9/13 client-connect: Move adding inotify watch into its own function
In reply to Arne SchwabeThis make the code a bit better readable and also prepares resuing the function for client-connect return files
--- src/openvpn/multi.c | 46 +++++++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 16 deletions(-)
src/openvpn/multi.c +30 -16
@@ -2696,6 +2696,32 @@ multi_schedule_context_wakeup(struct multi_context *m, struct multi_instance *mi compute_wakeup_sigma(&mi->context.c2.timeval)); } +#if defined(ENABLE_ASYNC_PUSH ) && defined(ENABLE_DEF_AUTH ) +static void +add_inotify_file_watch (struct multi_context *m,struct multi_instance *mi, +int inotify_fd,const char * file) +{ +/* watch acf file */ +long watch_descriptor =inotify_add_watch (inotify_fd, 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 |M_ERRNO ,"MULTI: inotify_add_watch error" ); + } +} +#endif +/* * Figure instance-specific timers, convert * earliest to absolute time in mi->wakeup,
@@ -2725,23 +2751,11 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const unspre_select (&mi->context);#if defined(ENABLE_ASYNC_PUSH ) && defined(ENABLE_DEF_AUTH ) -if (ks && ks->auth_control_file && ks->auth_deferred && !was_authenticated) +if (ks && ks->auth_control_file && + ks->auth_deferred && !was_authenticated) { -/* 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 |M_ERRNO ,"MULTI: inotify_add_watch error" ); - } +add_inotify_file_watch (m, mi, m->top.c2.inotify_fd, + ks->auth_control_file); }#endif
Signature · 1 line
2.19.1
PATCH v3 10/13 client-connect: Also use inotify for the deferred client-connect status file
In reply to Arne SchwabeAs we never do client-connect and authentication at the same time it is safe to reuse the existing fields for client-connect return status file
--- src/openvpn/multi.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
src/openvpn/multi.c +13 -3
@@ -2491,8 +2491,10 @@ multi_connection_established(struct multi_context *m, struct multi_instance *mi)#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. +* Called when inotify event is fired, which happens when acf +* or connect-status file is closed or deleted. +* Continues authentication and sends push_reply +* (or be deferred again by client-connect) */ void multi_process_file_closed (struct multi_context *m,const unsigned int mpp_flags)
@@ -2768,7 +2770,15 @@ multi_process_post(struct multi_context *m, struct multi_instance *mi, const uns {multi_connection_established (m, mi); } - +#if defined(ENABLE_ASYNC_PUSH ) && defined(ENABLE_DEF_AUTH ) +if (mi->client_connect_status !=CC_STATUS_ESTABLISHED && + mi->client_connect_defer_state.deferred_ret_file) + { +add_inotify_file_watch (m, mi, m->top.c2.inotify_fd, + mi->client_connect_defer_state. + deferred_ret_file); + } +#endif /* tell scheduler to wake us up at some point in the future */ multi_schedule_context_wakeup (m, mi); }
Signature · 1 line
2.19.1
PATCH v3 11/13 client-connect: Add deferred support to the client-connect plugin v1 handler
In reply to Arne SchwabeFrom: Fabian Knittel <[email protected]...>
Uses the infrastructure provided and used in the previous patch to provide deferral support to the v1 client-connect plugin handler as well.
PATCH V3: Modify the API to also (optionally) call the plugin on a deferred call. This allows the plugin authors to be more flexible and make the V1 API more similar to the V2 API.
--- include/openvpn-plugin.h.in | 30 ++++++------ src/openvpn/multi.c | 98 ++++++++++++++++++++++++++++--------- src/openvpn/plugin.c | 3 ++ 3 files changed, 93 insertions(+), 38 deletions(-)
include/openvpn-plugin.h.in +16 -14
@@ -116,20 +116,22 @@ extern "C" { * FUNC: openvpn_plugin_client_destructor_v1 (top-level "generic" client) * FUNC: openvpn_plugin_close_v1 */ -#define OPENVPN_PLUGIN_UP 0 -#define OPENVPN_PLUGIN_DOWN 1 -#define OPENVPN_PLUGIN_ROUTE_UP 2 -#define OPENVPN_PLUGIN_IPCHANGE 3 -#define OPENVPN_PLUGIN_TLS_VERIFY 4 -#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5 -#define OPENVPN_PLUGIN_CLIENT_CONNECT 6 -#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7 -#define OPENVPN_PLUGIN_LEARN_ADDRESS 8 -#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9 -#define OPENVPN_PLUGIN_TLS_FINAL 10 -#define OPENVPN_PLUGIN_ENABLE_PF 11 -#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12 -#define OPENVPN_PLUGIN_N 13 +#define OPENVPN_PLUGIN_UP 0 +#define OPENVPN_PLUGIN_DOWN 1 +#define OPENVPN_PLUGIN_ROUTE_UP 2 +#define OPENVPN_PLUGIN_IPCHANGE 3 +#define OPENVPN_PLUGIN_TLS_VERIFY 4 +#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY 5 +#define OPENVPN_PLUGIN_CLIENT_CONNECT 6 +#define OPENVPN_PLUGIN_CLIENT_DISCONNECT 7 +#define OPENVPN_PLUGIN_LEARN_ADDRESS 8 +#define OPENVPN_PLUGIN_CLIENT_CONNECT_V2 9 +#define OPENVPN_PLUGIN_TLS_FINAL 10 +#define OPENVPN_PLUGIN_ENABLE_PF 11 +#define OPENVPN_PLUGIN_ROUTE_PREDOWN 12 +#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER 13 +#define OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 14 +#define OPENVPN_PLUGIN_N 15 /* * Build a mask out of a set of plug-in types.
src/openvpn/multi.c +74 -24
141 lines not on this page · raw message
src/openvpn/plugin.c +3 -0
@@ -104,6 +104,9 @@ plugin_type_name(const int type)case OPENVPN_PLUGIN_CLIENT_CONNECT_V2 :return "PLUGIN_CLIENT_CONNECT" ; +case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER : +return "PLUGIN_CLIENT_CONNECT" ; +case OPENVPN_PLUGIN_CLIENT_DISCONNECT :return "PLUGIN_CLIENT_DISCONNECT" ;
Signature · 1 line
2.19.1
PATCH v3 12/13 client-connect: Implement deferred connect support for plugin API v2
In reply to Arne SchwabeThe V2 API is simpler than the V1 API since there is no passing of data via files. This also means that with the current API the V2 API cannot support async notify via files. Adding a file just for async notify seems very hacky and when needed we should implement a better option when async is needed for the plugin V2 API
--- src/openvpn/multi.c | 58 +++++++++++++++++++++++++++++++++++--------- src/openvpn/plugin.c | 3 +++ 2 files changed, 50 insertions(+), 11 deletions(-)
src/openvpn/multi.c +47 -11
@@ -2029,7 +2029,8 @@ multi_client_connect_call_plugin_v1_deferred(struct multi_context *m,static enum client_connect_return multi_client_connect_call_plugin_v2 (struct multi_context *m,struct multi_instance *mi, -unsigned int *option_types_found) +unsigned int *option_types_found, +bool deferred) {enum client_connect_return ret =CC_RET_SKIPPED ;#ifdef ENABLE_PLUGIN
@@ -2037,32 +2038,67 @@ multi_client_connect_call_plugin_v2(struct multi_context *m,ASSERT (mi);ASSERT (option_types_found); +int call = deferred ?OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 : +OPENVPN_PLUGIN_CLIENT_CONNECT_V2 ;/* V2 callback, use a plugin_return struct for passing back return info */ -if (plugin_defined (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 )) +if (plugin_defined (mi->context.plugins, call)) {struct plugin_return pr;plugin_return_init (&pr); -if (plugin_call (mi->context.plugins,OPENVPN_PLUGIN_CLIENT_CONNECT_V2 , -NULL , &pr, mi->context.c2.es) - !=OPENVPN_PLUGIN_FUNC_SUCCESS ) +int plug_ret =plugin_call (mi->context.plugins, call, +NULL , &pr, mi->context.c2.es); +if (plug_ret ==OPENVPN_PLUGIN_FUNC_SUCCESS ) { -msg (M_WARN ,"WARNING: client-connect-v2 plugin call failed" ); - ret =CC_RET_FAILED ; +multi_client_connect_post_plugin (m, mi, &pr, option_types_found); + ret =CC_RET_SUCCEEDED ; + } +else if (plug_ret ==OPENVPN_PLUGIN_FUNC_DEFERRED ) + { + ret =CC_RET_DEFERRED ; +if (!(plugin_defined (mi->context.plugins, +OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 ))) + { +msg (M_WARN ,"A plugin that defers from the " +"OPENVPN_PLUGIN_CLIENT_CONNECT_V2 call must also " +"declare support for " +"OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2" ); + ret =CC_RET_FAILED ; + } }else { -multi_client_connect_post_plugin (m, mi, &pr, option_types_found); - ret =CC_RET_SUCCEEDED ; +msg (M_WARN ,"WARNING: client-connect-v2 plugin call failed" ); + ret =CC_RET_FAILED ; } +plugin_return_free (&pr); }#endif /* ifdef ENABLE_PLUGIN */ return ret; } + +static enum client_connect_return +multi_client_connect_call_plugin_v2_initial (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found) +{ +return multi_client_connect_call_plugin_v2 (m, mi, option_types_found, + false); +} + +static enum client_connect_return +multi_client_connect_call_plugin_v2_deferred (struct multi_context *m, +struct multi_instance *mi, +unsigned int *option_types_found) +{ +return multi_client_connect_call_plugin_v2 (m, mi, option_types_found, + true); +} +/** * Runs the --client-connect script if one is defined. */
@@ -2374,8 +2410,8 @@ static const struct client_connect_handlers client_connect_handlers[] = { .deferred = multi_client_connect_call_plugin_v1_deferred, }, { - .main = multi_client_connect_call_plugin_v2, - .deferred = multi_client_connect_fail + .main = multi_client_connect_call_plugin_v2_initial, + .deferred = multi_client_connect_call_plugin_v2_deferred, }, { .main = multi_client_connect_call_script,
src/openvpn/plugin.c +3 -0
@@ -107,6 +107,9 @@ plugin_type_name(const int type)case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER :return "PLUGIN_CLIENT_CONNECT" ; +case OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 : +return "PLUGIN_CLIENT_CONNECT" ; +case OPENVPN_PLUGIN_CLIENT_DISCONNECT :return "PLUGIN_CLIENT_DISCONNECT" ;
Signature · 1 line
2.19.1
PATCH v3 13/13 client-connect: Add documentation for the deferred client connect feature
In reply to Arne Schwabe--- doc/openvpn.8 | 47 ++++++++++++++++++++++++++++++++++--- include/openvpn-plugin.h.in | 21 ++++++++++++----- 2 files changed, 59 insertions(+), 9 deletions(-)
doc/openvpn.8 +44 -3
@@ -3346,6 +3346,13 @@ is significant. If .B script returns a non\-zero error status, it will cause the client to be disconnected. + +If a +.B \-\-client\-connect cmd +wants to defer the generating the configuration the script should +use the client_connect_deferred_file and client_connect_config_file +environment variables and write status acorrdingly into these files +(See the environment section below for more details). .\"********************************************************* .TP .B \-\-client\-disconnect cmd
@@ -3428,12 +3435,18 @@ This directory will be used by in the following cases: * .B \-\-client\-connect -scripts to dynamically generate client\-specific -configuration files. +scripts and +.B OPENVPN_PLUGIN_CLIENT_CONNECT +plugin hook +to dynamically generate client\-specific configuration files +and return success/failure via client_connect_deferred_file +when using deferred client connect method * .B OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY -plugin hook to return success/failure via auth_control_file +and + +plugin hook to return success/failure via auth_control_file/ when using deferred auth method *
@@ -6431,6 +6444,34 @@ Set prior to execution of the script. .\"********************************************************* .TP +.B client_connect_config_file +The path to the configuration file that should be written by +the +.B \-\-client\-connect +The content of this enviroment variable is identical to the +file as a argument of the called +.B \-\-client\-connect +script. +.\"********************************************************* +.TP +.B client_connect_defferred_file +This file can be optionally written to communicate a status +code of the +.TP +.B \-\-client\-connect +script. If used for deferring, this file must be written +before the +.B \-\-client\-connect +script exits. The first character in the file is interpreted +and 1 is equal to normal script execution, 0 indicated and +error (in the same way that a non zero exit status does) and +2 indicates that the script deferred returning the config +file. When the script defers the executing it must first write 2 +during the execution of the script. A background process or similar +must then take of writing the client_connect_config_file and when +finished, write the a 1 to this file. +.\"********************************************************* +.TP .B common_name The X509 common name of an authenticated client. Set prior to execution of
include/openvpn-plugin.h.in +15 -6
@@ -557,12 +557,21 @@ OPENVPN_PLUGIN_DEF openvpn_plugin_handle_t OPENVPN_PLUGIN_FUNC(openvpn_plugin_op * OPENVPN_PLUGIN_FUNC_SUCCESS on success, OPENVPN_PLUGIN_FUNC_ERROR on failure * * In addition, OPENVPN_PLUGIN_FUNC_DEFERRED may be returned by - * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY. This enables asynchronous - * authentication where the plugin (or one of its agents) may indicate - * authentication success/failure some number of seconds after the return - * of the OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY handler by writing a single - * char to the file named by auth_control_file in the environmental variable - * list (envp). + * OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY, OPENVPN_PLUGIN_CLIENT_CONNECT and + * OPENVPN_PLUGIN_CLIENT_CONNECT_V2. This enables asynchronous + * authentication or client connect where the plugin (or one of its agents) + * may indicate authentication success/failure or client configuration some + * number of seconds after the return of the function handler. + * For OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY and OPENVPN_PLUGIN_CLIENT_CONNECT + * this done by writing a single char to the file named by + * auth_control_file/client_connect_deferred_file + * in the environmental variable list (envp). + * + * In Addition the OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER and + * OPENVPN_PLUGIN_CLIENT_CONNECT_DEFER_V2 are called when OpenVPN tries to + * get the deferred result. For V2 call implementing this function is + * required as information is not passed by files. For the normal version + * the call is optional. * * first char of auth_control_file: * '0' -- indicates auth failure
Signature · 1 line
2.19.1