Hi Arne, following your lead I recompiled with --enable-async-push and the problem went away.
Some ifdef is messing up the code path? Cheers, On 06/05/2021 23:49, Antonio Quartulli wrote: > 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