Skip to content

Commit e34094e

Browse files
lstipakovJenkins-dev
authored andcommitted
Refactor reauthentication logic
The reauthentication logic differs from openvpn2 and the code is a bit hard to follow. Simplify the code and make it behave like in openvpn2. - password is cached by default - password is purged when auth-nocache is presented in a local config or pushed - when AUTH_FAILED is received and we have no session-id, throw a fatal error - when AUTH_FAILED is received and user interaction is required for authentication (MFA), throw a fatal error - when AUTH_FAILED is received, user interaction is not required for authentication and either we have a cached password OR password is not needed, we reconnect. Password is "needed" when non-empty password is provided. User interaction is required for static/dynamic challenge and SAML. Signed-off-by: Lev Stipakov <[email protected]>
1 parent d0cfea2 commit e34094e

File tree

12 files changed

+191
-144
lines changed

12 files changed

+191
-144
lines changed

client/ovpncli.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -795,13 +795,12 @@ OPENVPN_CLIENT_EXPORT Status OpenVPNClient::provide_creds(const ProvideCreds &cr
795795
{
796796
ClientCreds::Ptr cc = new ClientCreds();
797797
cc->set_username(creds.username);
798+
cc->save_username_for_session_id();
798799
cc->set_password(creds.password);
799800
cc->set_http_proxy_username(creds.http_proxy_user);
800801
cc->set_http_proxy_password(creds.http_proxy_pass);
801802
cc->set_response(creds.response);
802803
cc->set_dynamic_challenge_cookie(creds.dynamicChallengeCookie, creds.username);
803-
cc->set_replace_password_with_session_id(creds.replacePasswordWithSessionID);
804-
cc->enable_password_cache(creds.cachePassword);
805804
state->creds = cc;
806805
}
807806
catch (const std::exception &e)
@@ -972,15 +971,6 @@ OPENVPN_CLIENT_EXPORT void OpenVPNClient::connect_setup(Status &status, bool &se
972971
#if defined(OPENVPN_EXTERNAL_TRANSPORT_FACTORY)
973972
cc.extern_transport_factory = this;
974973
#endif
975-
// force Session ID use and disable password cache if static challenge is enabled
976-
if (state->creds
977-
&& !state->creds->get_replace_password_with_session_id()
978-
&& !state->eval.autologin
979-
&& !state->eval.staticChallenge.empty())
980-
{
981-
state->creds->set_replace_password_with_session_id(true);
982-
state->creds->enable_password_cache(false);
983-
}
984974

985975
// external PKI
986976
#if !defined(USE_APPLE_SSL)

client/ovpncli.hpp

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,19 +119,6 @@ struct ProvideCreds
119119

120120
// Dynamic challenge/response cookie
121121
std::string dynamicChallengeCookie;
122-
123-
// If true, on successful connect, we will replace the password
124-
// with the session ID we receive from the server (if provided).
125-
// If false, the password will be cached for future reconnects
126-
// and will not be replaced with a session ID, even if the
127-
// server provides one.
128-
bool replacePasswordWithSessionID = false;
129-
130-
// If true, and if replacePasswordWithSessionID is true, and if
131-
// we actually receive a session ID from the server, cache
132-
// the user-provided password for future use before replacing
133-
// the active password with the session ID.
134-
bool cachePassword = false;
135122
};
136123

137124
// used to get session token from VPN core

openvpn/client/cliconnect.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,7 @@ class ClientConnect : ClientProto::NotifyCallback,
463463
// Errors below will cause the client to NOT retry the connection,
464464
// or otherwise give the error special handling.
465465

466+
case Error::SESSION_EXPIRED:
466467
case Error::AUTH_FAILED:
467468
{
468469
const std::string &reason = client->fatal_reason();
@@ -474,9 +475,13 @@ class ClientConnect : ClientProto::NotifyCallback,
474475
}
475476
else
476477
{
477-
ClientEvent::Base::Ptr ev = new ClientEvent::AuthFailed(reason);
478+
ClientEvent::Base::Ptr ev;
479+
if (client->fatal() == Error::SESSION_EXPIRED)
480+
ev = new ClientEvent::SessionExpired(reason);
481+
else
482+
ev = new ClientEvent::AuthFailed(reason);
478483
client_options->events().add_event(std::move(ev));
479-
client_options->stats().error(Error::AUTH_FAILED);
484+
client_options->stats().error(client->fatal());
480485
if (client_options->retry_on_auth_failed())
481486
queue_restart(5000);
482487
else

openvpn/client/clicreds.hpp

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,7 @@ class ClientCreds : public RC<thread_unsafe_refcount>
4242
public:
4343
typedef RCPtr<ClientCreds> Ptr;
4444

45-
ClientCreds()
46-
: allow_cache_password(false),
47-
password_save_defined(false),
48-
replace_password_with_session_id(false),
49-
did_replace_password_with_session_id(false)
50-
{
51-
}
45+
ClientCreds() = default;
5246

5347
void set_username(const std::string &username_arg)
5448
{
@@ -58,7 +52,10 @@ class ClientCreds : public RC<thread_unsafe_refcount>
5852
void set_password(const std::string &password_arg)
5953
{
6054
password = password_arg;
61-
did_replace_password_with_session_id = false;
55+
if (!password.empty())
56+
{
57+
password_needed_ = true;
58+
}
6259
}
6360

6461
void set_http_proxy_username(const std::string &username)
@@ -74,6 +71,10 @@ class ClientCreds : public RC<thread_unsafe_refcount>
7471
void set_response(const std::string &response_arg)
7572
{
7673
response = response_arg;
74+
if (!response.empty())
75+
{
76+
need_user_interaction_ = true;
77+
}
7778
}
7879

7980
void set_dynamic_challenge_cookie(const std::string &cookie, const std::string &username)
@@ -82,51 +83,31 @@ class ClientCreds : public RC<thread_unsafe_refcount>
8283
dynamic_challenge.reset(new ChallengeResponse(cookie, username));
8384
}
8485

85-
void set_replace_password_with_session_id(const bool value)
86-
{
87-
replace_password_with_session_id = value;
88-
}
89-
90-
void enable_password_cache(const bool value)
91-
{
92-
allow_cache_password = value;
93-
}
94-
95-
bool get_replace_password_with_session_id() const
96-
{
97-
return replace_password_with_session_id;
98-
}
99-
10086
void set_session_id(const std::string &user, const std::string &sess_id)
10187
{
102-
// force Session ID use if dynamic challenge is enabled
103-
if (dynamic_challenge && !replace_password_with_session_id)
104-
replace_password_with_session_id = true;
105-
106-
if (replace_password_with_session_id)
88+
if (dynamic_challenge)
10789
{
108-
if (allow_cache_password && !password_save_defined)
109-
{
110-
password_save = password;
111-
password_save_defined = true;
112-
}
113-
password = sess_id;
114-
response = "";
115-
if (dynamic_challenge)
116-
{
117-
username = dynamic_challenge->get_username();
118-
dynamic_challenge.reset();
119-
}
120-
else if (!user.empty())
121-
username = user;
122-
did_replace_password_with_session_id = true;
90+
session_id_username = dynamic_challenge->get_username();
91+
// for dynamic challenge we use dynamic password only once
92+
dynamic_challenge.reset();
93+
}
94+
else if (!user.empty())
95+
{
96+
session_id_username = user;
12397
}
98+
99+
// response is used only once
100+
response.clear();
101+
102+
session_id = sess_id;
124103
}
125104

126105
std::string get_username() const
127106
{
128107
if (dynamic_challenge)
129108
return dynamic_challenge->get_username();
109+
else if (!session_id_username.empty())
110+
return session_id_username;
130111
else
131112
return username;
132113
}
@@ -136,7 +117,12 @@ class ClientCreds : public RC<thread_unsafe_refcount>
136117
if (dynamic_challenge)
137118
return dynamic_challenge->construct_dynamic_password(response);
138119
else if (response.empty())
139-
return password;
120+
{
121+
if (!session_id.empty())
122+
return session_id;
123+
else
124+
return password;
125+
}
140126
else
141127
return ChallengeResponse::construct_static_password(password, response);
142128
}
@@ -173,34 +159,44 @@ class ClientCreds : public RC<thread_unsafe_refcount>
173159

174160
bool session_id_defined() const
175161
{
176-
return did_replace_password_with_session_id;
162+
return !session_id.empty();
177163
}
178164

179-
// If we have a saved password that is not a session ID,
180-
// restore it and wipe any existing session ID.
181-
bool reset_to_cached_password()
165+
void purge_session_id()
182166
{
183-
if (password_save_defined)
184-
{
185-
password = password_save;
186-
password_save.clear();
187-
password_save_defined = false;
188-
did_replace_password_with_session_id = false;
189-
return true;
190-
}
191-
else
192-
return false;
167+
session_id.clear();
168+
session_id_username.clear();
193169
}
194170

195-
void purge_session_id()
171+
void purge_user_pass()
172+
{
173+
username.clear();
174+
password.clear();
175+
}
176+
177+
void save_username_for_session_id()
196178
{
197-
if (!reset_to_cached_password())
179+
if (session_id_username.empty())
198180
{
199-
password.clear();
200-
did_replace_password_with_session_id = false;
181+
session_id_username = username;
201182
}
202183
}
203184

185+
void set_need_user_interaction()
186+
{
187+
need_user_interaction_ = true;
188+
}
189+
190+
bool need_user_interaction() const
191+
{
192+
return need_user_interaction_;
193+
}
194+
195+
bool password_needed() const
196+
{
197+
return password_needed_;
198+
}
199+
204200
std::string auth_info() const
205201
{
206202
std::string ret;
@@ -210,20 +206,27 @@ class ClientCreds : public RC<thread_unsafe_refcount>
210206
}
211207
else if (response.empty())
212208
{
213-
if (!username.empty())
209+
if (!session_id_username.empty() || !username.empty())
210+
{
214211
ret += "Username";
212+
}
215213
else
214+
{
216215
ret += "UsernameEmpty";
216+
}
217217
ret += '/';
218-
if (!password.empty())
218+
if (!session_id.empty())
219+
{
220+
ret += "SessionID";
221+
}
222+
else if (!password.empty())
219223
{
220-
if (did_replace_password_with_session_id)
221-
ret += "SessionID";
222-
else
223-
ret += "Password";
224+
ret += "Password";
224225
}
225226
else
227+
{
226228
ret += "PasswordEmpty";
229+
}
227230
}
228231
else
229232
{
@@ -241,23 +244,20 @@ class ClientCreds : public RC<thread_unsafe_refcount>
241244
std::string http_proxy_user;
242245
std::string http_proxy_pass;
243246

244-
// Password caching
245-
bool allow_cache_password;
246-
bool password_save_defined;
247-
std::string password_save;
247+
std::string session_id;
248+
std::string session_id_username;
248249

249-
// Response to challenge
250+
// Response to a challenge
250251
std::string response;
251252

252-
// Info describing a dynamic challenge
253-
ChallengeResponse::Ptr dynamic_challenge;
253+
// Need user interaction to authenticate - such as static/dynamic challenge or SAML
254+
bool need_user_interaction_ = false;
254255

255-
// If true, on successful connect, we will replace the password
256-
// with the session ID we receive from the server.
257-
bool replace_password_with_session_id;
256+
// Non-empty password provided
257+
bool password_needed_ = false;
258258

259-
// true if password has been replaced with Session ID
260-
bool did_replace_password_with_session_id;
259+
// Info describing a dynamic challenge
260+
ChallengeResponse::Ptr dynamic_challenge;
261261
};
262262

263263
} // namespace openvpn

openvpn/client/clievent.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ enum Type
9292
RELAY_ERROR,
9393
COMPRESS_ERROR,
9494
NTLM_MISSING_CRYPTO,
95+
SESSION_EXPIRED,
9596

9697
N_TYPES
9798
};
@@ -153,7 +154,8 @@ inline const char *event_name(const Type type)
153154
"EPKI_INVALID_ALIAS",
154155
"RELAY_ERROR",
155156
"COMPRESS_ERROR",
156-
"NTLM_MISSING_CRYPTO"};
157+
"NTLM_MISSING_CRYPTO",
158+
"SESSION_EXPIRED"};
157159

158160
static_assert(N_TYPES == array_size(names), "event names array inconsistency");
159161
if (type < N_TYPES)
@@ -451,6 +453,14 @@ struct AuthFailed : public ReasonBase
451453
}
452454
};
453455

456+
struct SessionExpired : public ReasonBase
457+
{
458+
SessionExpired(std::string reason)
459+
: ReasonBase(SESSION_EXPIRED, std::move(reason))
460+
{
461+
}
462+
};
463+
454464
struct CertVerifyFail : public ReasonBase
455465
{
456466
CertVerifyFail(std::string reason)

0 commit comments

Comments
 (0)