Hi Arne, after our discussion on IRC I understood you expected this patch to not change the server behaviour.
If something is suboptimal, it means it was suboptimal also before this patch. However, with your patch I can clearly see a longer delay in returning the AUTH result to the client, in case of deferred auth. Without your patch (initial lines are the output of my auth-verify script): PASS=testno FILE=/tmp/openvpn_acf_662a1165ab3293aa3fb053050e1c2463.tmp FAIL 2021-05-06 23:40:28 10.10.10.2:1194 TLS: Username/Password authentication deferred for username 'test' 2021-05-06 23:40:28 10.10.10.2:1194 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 384 bit EC, curve secp384r1, signature: ecdsa-with-SHA256 2021-05-06 23:40:28 10.10.10.2:1194 [client] Peer Connection Initiated with [AF_INET6]::ffff:10.10.10.2:1194 2021-05-06 23:40:29 10.10.10.2:1194 PUSH: Received control message: 'PUSH_REQUEST' 2021-05-06 23:40:29 10.10.10.2:1194 Delayed exit in 5 seconds 2021-05-06 23:40:29 10.10.10.2:1194 SENT CONTROL [client]: 'AUTH_FAILED' (status=1) My script uses the deferred logic, but it is super fast (as you can see it fails before the server can even do anything else). Later, as soon as the first PUSH_REQUEST is received, the server replies immediately with an AUTH_FAILED. With your patch (script and everything else stays the same): PASS=testno FILE=/tmp/openvpn_acf_4cdab09a9640e8eb4cacadfc1734bb2c.tmp FAIL 2021-05-06 23:45:22 10.10.10.2:1194 TLS: Username/Password authentication deferred for username 'test' 2021-05-06 23:45:22 10.10.10.2:1194 Control Channel: TLSv1.3, cipher TLSv1.3 TLS_AES_256_GCM_SHA384, peer certificate: 384 bit EC, curve secp384r1, signature: ecdsa-with-SHA256 2021-05-06 23:45:22 10.10.10.2:1194 [client] Peer Connection Initiated with [AF_INET6]::ffff:10.10.10.2:1194 2021-05-06 23:45:23 10.10.10.2:1194 PUSH: Received control message: 'PUSH_REQUEST' 2021-05-06 23:45:28 10.10.10.2:1194 PUSH: Received control message: 'PUSH_REQUEST' 2021-05-06 23:45:33 10.10.10.2:1194 Delayed exit in 5 seconds 2021-05-06 23:45:33 10.10.10.2:1194 SENT CONTROL [client]: 'AUTH_FAILED' (status=1) As you can see the script does the deferred auth immediately, but the server send the AUTH_FAILED only after the PUSH_REQUEST being received *after some (10?) seconds since the failure of the script*. To me this sounds like the server is caching the current status for some (10?) seconds and it is not updating it with the actual result. However, this does not seem to happen without your patch as the server is probably forcing a reload of the result from file everytime a PUSH_REQUEST is received. I believe this behaviour should stay the same. Regards, On 06/05/2021 16:12, Arne Schwabe wrote: > tls_authentication_status does caching to avoid file I/O more than > every TLS_MULTI_AUTH_STATUS_INTERVAL (10s) per connection. But > counter-intuitively it does not return the cached result but rather > TLS_AUTHENTICATION_UNDEFINED if the cache is not refreshed by the call. > > This is workarounded by forcing a refresh in some areas of the code > (latency = 0). > > This patch changes the behaviour by always returning the last known > status and only updating the file status when the i/o timeout for the > caches is reached. > > The old logic in send_auth_failed is fragile in the sense that if > it is called again while an exit is scheduled it will reset the timer > to 5s again. Since we now always report the status from > tls_authentication_status() instead only every 10s, this caused OpenVPN > to infinitively reset the timer. Fix this by only setting the status > if no exit is scheduled. The function is still called multiple times but > since it is with coarse timer frequency, the 4 extra calls (1 per second) > are better than to add more extra code to avoid these calls. > > The patch also changes the DEFINE enum into a real enum. > > Patch v2: only update tas_cache_last_udpate when actually updating the cache. > Patch v3: avoid rearming timer > > Signed-off-by: Arne Schwabe <a...@rfc2549.org> > --- > src/openvpn/multi.c | 2 +- > src/openvpn/push.c | 11 ++++++++- > src/openvpn/ssl_common.h | 16 +++++++++--- > src/openvpn/ssl_verify.c | 53 ++++++++++++++++++++++------------------ > src/openvpn/ssl_verify.h | 3 +-- > 5 files changed, 54 insertions(+), 31 deletions(-) > > diff --git a/src/openvpn/multi.c b/src/openvpn/multi.c > index 666456da9..ab2270a58 100644 > --- a/src/openvpn/multi.c > +++ b/src/openvpn/multi.c > @@ -2596,7 +2596,7 @@ static const multi_client_connect_handler > client_connect_handlers[] = { > static void > multi_connection_established(struct multi_context *m, struct multi_instance > *mi) > { > - if (tls_authentication_status(mi->context.c2.tls_multi, 0) > + if (tls_authentication_status(mi->context.c2.tls_multi, > TLS_MULTI_AUTH_STATUS_INTERVAL) > != TLS_AUTHENTICATION_SUCCEEDED) > { > return; > diff --git a/src/openvpn/push.c b/src/openvpn/push.c > index fcafc5003..671220c3f 100644 > --- a/src/openvpn/push.c > +++ b/src/openvpn/push.c > @@ -335,10 +335,18 @@ __attribute__ ((format(__printf__, 4, 5))) > > /* > * Send auth failed message from server to client. > + * > + * Does nothing if an exit is already scheduled > */ > void > send_auth_failed(struct context *c, const char *client_reason) > { > + if (event_timeout_defined(&c->c2.scheduled_exit)) > + { > + msg(D_TLS_DEBUG, "exit already scheduled for context"); > + return; > + } > + > struct gc_arena gc = gc_new(); > static const char auth_failed[] = "AUTH_FAILED"; > size_t len; > @@ -855,7 +863,8 @@ process_incoming_push_request(struct context *c) > { > int ret = PUSH_MSG_ERROR; > > - if (tls_authentication_status(c->c2.tls_multi, 0) == > TLS_AUTHENTICATION_FAILED > + > + if (tls_authentication_status(c->c2.tls_multi, > TLS_MULTI_AUTH_STATUS_INTERVAL) == TLS_AUTHENTICATION_FAILED > || c->c2.tls_multi->multi_state == CAS_FAILED) > { > const char *client_reason = tls_client_reason(c->c2.tls_multi); > diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h > index 9c923f2a6..52488a163 100644 > --- a/src/openvpn/ssl_common.h > +++ b/src/openvpn/ssl_common.h > @@ -152,6 +152,15 @@ struct auth_deferred_status > unsigned int auth_control_status; > }; > > +/* key_state_test_auth_control_file return values, these specify the > + * current status of a deferred authentication */ > +enum auth_deferred_result { > + ACF_PENDING, /**< deferred auth still pending */ > + ACF_SUCCEEDED, /**< deferred auth has suceeded */ > + ACF_DISABLED, /**< deferred auth is not used */ > + ACF_FAILED /**< deferred auth has failed */ > +}; > + > /** > * Security parameter state of one TLS and data channel %key session. > * @ingroup control_processor > @@ -219,7 +228,7 @@ struct key_state > > #ifdef ENABLE_MANAGEMENT > unsigned int mda_key_id; > - unsigned int mda_status; > + enum auth_deferred_result mda_status; > #endif > time_t acf_last_mod; > > @@ -561,8 +570,9 @@ struct tls_multi > char *locked_username; > struct cert_hash_set *locked_cert_hash_set; > > - /* Time of last call to tls_authentication_status */ > - time_t tas_last; > + /* Time of last when we updated the cached state of > + * tls_authentication_status deferred files */ > + time_t tas_cache_last_update; > > /* > * An error message to send to client on AUTH_FAILED > diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c > index 34ee19caf..b1b01f777 100644 > --- a/src/openvpn/ssl_verify.c > +++ b/src/openvpn/ssl_verify.c > @@ -845,13 +845,6 @@ cleanup: > * user/password authentication. > *************************************************************************** > */ > > -/* 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 > - > void > auth_set_client_reason(struct tls_multi *multi, const char *client_reason) > { > @@ -866,7 +859,7 @@ auth_set_client_reason(struct tls_multi *multi, const > char *client_reason) > > #ifdef ENABLE_MANAGEMENT > > -static inline unsigned int > +static inline enum auth_deferred_result > man_def_auth_test(const struct key_state *ks) > { > if (management_enable_def_auth(management)) > @@ -1041,13 +1034,23 @@ key_state_gen_auth_control_files(struct > auth_deferred_status *ads, > return (acf && apf); > } > > -static unsigned int > -key_state_test_auth_control_file(struct auth_deferred_status *ads) > +/** > + * Checks the control status from a file. The function will try to read > + * and update the cached status if the status is still pending and the > paramter > + * cached is false. The function returns the > + * > + * > + * @param ads deferred status control structure > + * @param cached Return only cached status > + * @return > + */ > +static enum auth_deferred_result > +key_state_test_auth_control_file(struct auth_deferred_status *ads, bool > cached) > { > if (ads->auth_control_file) > { > unsigned int ret = ads->auth_control_status; > - if (ret == ACF_UNDEFINED) > + if (ret == ACF_PENDING && !cached) > { > FILE *fp = fopen(ads->auth_control_file, "r"); > if (fp) > @@ -1084,11 +1087,7 @@ tls_authentication_status(struct tls_multi *multi, > const int latency) > /* at least one key already failed authentication */ > bool failed_auth = false; > > - if (latency && multi->tas_last + latency >= now) > - { > - return TLS_AUTHENTICATION_UNDEFINED; > - } > - multi->tas_last = now; > + bool cached = multi->tas_cache_last_update + latency >= now; > > for (int i = 0; i < KEY_SCAN_SIZE; ++i) > { > @@ -1102,11 +1101,11 @@ tls_authentication_status(struct tls_multi *multi, > const int latency) > } > else > { > - unsigned int auth_plugin = ACF_DISABLED; > - unsigned int auth_script = ACF_DISABLED; > - unsigned int auth_man = ACF_DISABLED; > - auth_plugin = > key_state_test_auth_control_file(&ks->plugin_auth); > - auth_script = > key_state_test_auth_control_file(&ks->script_auth); > + enum auth_deferred_result auth_plugin = ACF_DISABLED; > + enum auth_deferred_result auth_script = ACF_DISABLED; > + enum auth_deferred_result auth_man = ACF_DISABLED; > + auth_plugin = > key_state_test_auth_control_file(&ks->plugin_auth, cached); > + auth_script = > key_state_test_auth_control_file(&ks->script_auth, cached); > #ifdef ENABLE_MANAGEMENT > auth_man = man_def_auth_test(ks); > #endif > @@ -1118,9 +1117,9 @@ tls_authentication_status(struct tls_multi *multi, > const int latency) > ks->authenticated = KS_AUTH_FALSE; > failed_auth = true; > } > - else if (auth_plugin == ACF_UNDEFINED > - || auth_script == ACF_UNDEFINED > - || auth_man == ACF_UNDEFINED) > + else if (auth_plugin == ACF_PENDING > + || auth_script == ACF_PENDING > + || auth_man == ACF_PENDING) > { > if (now < ks->auth_deferred_expire) > { > @@ -1140,6 +1139,12 @@ tls_authentication_status(struct tls_multi *multi, > const int latency) > } > } > > + /* we did not rely on a cached result, remember the cache update time */ > + if (!cached) > + { > + multi->tas_cache_last_update = now; > + } > + > #if 0 > dmsg(D_TLS_ERRORS, "TAS: a=%d s=%d d=%d f=%d", active, success, > deferred, failed_auth); > #endif > diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h > index 8358fb986..06b88b568 100644 > --- a/src/openvpn/ssl_verify.h > +++ b/src/openvpn/ssl_verify.h > @@ -69,8 +69,7 @@ enum tls_auth_status > { > TLS_AUTHENTICATION_SUCCEEDED=0, > TLS_AUTHENTICATION_FAILED=1, > - TLS_AUTHENTICATION_DEFERRED=2, > - TLS_AUTHENTICATION_UNDEFINED=3 > + TLS_AUTHENTICATION_DEFERRED=2 > }; > > /** > -- Antonio Quartulli _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel