Skip to content

Commit

Permalink
Protect cached username, password and token on client
Browse files Browse the repository at this point in the history
Keep the memory segment containing username and password in
"struct user_pass" encrypted. Works only on Windows.

Username and auth-token cached by the server are not covered
here.

v2: Encrypt username and password separately as it looks more
robust. We continue to depend on the username and password buffer
sizes to be a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE = 16,
which is the case now. An error is logged if this is not the case.

v3: move up ASSERT in auth_token.c

Change-Id: I42e17e09a02f01aedadc2b03f9527967f6e1e8ff
Signed-off-by: Selva Nair <[email protected]>
Acked-by: Frank Lichtenheld <[email protected]>
Message-Id: <[email protected]>
URL: https://www.mail-archive.com/[email protected]/msg29079.html
Signed-off-by: Gert Doering <[email protected]>
(cherry picked from commit 12a9c35)
  • Loading branch information
selvanair authored and cron2 committed Sep 8, 2024
1 parent 534609a commit 9e1598d
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/openvpn/auth_token.c
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ verify_auth_token(struct user_pass *up, struct tls_multi *multi,
* Base64 is <= input and input is < USER_PASS_LEN, so using USER_PASS_LEN
* is safe here but a bit overkill
*/
ASSERT(up && !up->protected);
uint8_t b64decoded[USER_PASS_LEN];
int decoded_len = openvpn_base64_decode(up->password + strlen(SESSION_ID_PREFIX),
b64decoded, USER_PASS_LEN);
Expand Down
63 changes: 56 additions & 7 deletions src/openvpn/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ get_user_pass_cr(struct user_pass *up,
bool password_from_stdin = false;
bool response_from_stdin = true;

unprotect_user_pass(up);
if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
{
msg(M_WARN, "Note: previous '%s' credentials failed", prefix);
Expand Down Expand Up @@ -479,14 +480,18 @@ purge_user_pass(struct user_pass *up, const bool force)
secure_memzero(up, sizeof(*up));
up->nocache = nocache;
}
/*
* don't show warning if the pass has been replaced by a token: this is an
* artificial "auth-nocache"
*/
else if (!warn_shown)
else
{
msg(M_WARN, "WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this");
warn_shown = true;
protect_user_pass(up);
/*
* don't show warning if the pass has been replaced by a token: this is an
* artificial "auth-nocache"
*/
if (!warn_shown)
{
msg(M_WARN, "WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this");
warn_shown = true;
}
}
}

Expand All @@ -495,6 +500,7 @@ set_auth_token(struct user_pass *tk, const char *token)
{
if (strlen(token))
{
unprotect_user_pass(tk);
strncpynt(tk->password, token, USER_PASS_LEN);
tk->token_defined = true;

Expand All @@ -505,6 +511,7 @@ set_auth_token(struct user_pass *tk, const char *token)
{
tk->defined = true;
}
protect_user_pass(tk);
}
}

Expand All @@ -513,6 +520,7 @@ set_auth_token_user(struct user_pass *tk, const char *username)
{
if (strlen(username))
{
unprotect_user_pass(tk);
/* Clear the username before decoding to ensure no old material is left
* and also allow decoding to not use all space to ensure the last byte is
* always 0 */
Expand All @@ -523,6 +531,7 @@ set_auth_token_user(struct user_pass *tk, const char *username)
{
msg(D_PUSH, "Error decoding auth-token-username");
}
protect_user_pass(tk);
}
}

Expand Down Expand Up @@ -799,3 +808,43 @@ prepend_dir(const char *dir, const char *path, struct gc_arena *gc)

return combined_path;
}

void
protect_user_pass(struct user_pass *up)
{
if (up->protected)
{
return;
}
#ifdef _WIN32
if (protect_buffer_win32(up->username, sizeof(up->username))
&& protect_buffer_win32(up->password, sizeof(up->password)))
{
up->protected = true;
}
else
{
purge_user_pass(up, true);
}
#endif
}

void
unprotect_user_pass(struct user_pass *up)
{
if (!up->protected)
{
return;
}
#ifdef _WIN32
if (unprotect_buffer_win32(up->username, sizeof(up->username))
&& unprotect_buffer_win32(up->password, sizeof(up->password)))
{
up->protected = false;
}
else
{
purge_user_pass(up, true);
}
#endif
}
14 changes: 14 additions & 0 deletions src/openvpn/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ struct user_pass
* use this second bool to track if the token (password) is defined */
bool token_defined;
bool nocache;
bool protected;

/* max length of username/password */
#ifdef ENABLE_PKCS11
Expand Down Expand Up @@ -204,6 +205,19 @@ get_num_elements(const char *string, char delimiter);
struct buffer
prepend_dir(const char *dir, const char *path, struct gc_arena *gc);

/**
* Encrypt username and password buffers in user_pass
*/
void
protect_user_pass(struct user_pass *up);

/**
* Decrypt username and password buffers in user_pass
*/
void
unprotect_user_pass(struct user_pass *up);


#define _STRINGIFY(S) #S
/* *INDENT-OFF* - uncrustify need to ignore this macro */
#define MAC_FMT _STRINGIFY(%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx)
Expand Down
4 changes: 3 additions & 1 deletion src/openvpn/proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,14 @@ get_user_pass_http(struct http_proxy_info *p, const bool force)
UP_TYPE_PROXY,
flags);
static_proxy_user_pass.nocache = p->options.nocache;
protect_user_pass(&static_proxy_user_pass);
}

/*
* Using cached credentials
*/
p->queried_creds = true;
p->up = static_proxy_user_pass;
p->up = static_proxy_user_pass; /* this is a copy of protected memory */
}

#if 0
Expand Down Expand Up @@ -668,6 +669,7 @@ establish_http_proxy_passthru(struct http_proxy_info *p,
{
clear_user_pass_http();
}
unprotect_user_pass(&p->up);
}

/* are we being called again after getting the digest server nonce in the previous transaction? */
Expand Down
7 changes: 7 additions & 0 deletions src/openvpn/ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ static struct user_pass passbuf; /* GLOBAL */
void
pem_password_setup(const char *auth_file)
{
unprotect_user_pass(&passbuf);
if (!strlen(passbuf.password))
{
get_user_pass(&passbuf, auth_file, UP_TYPE_PRIVATE_KEY, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_PASSWORD_ONLY);
Expand All @@ -420,6 +421,7 @@ pem_password_callback(char *buf, int size, int rwflag, void *u)
{
/* prompt for password even if --askpass wasn't specified */
pem_password_setup(NULL);
ASSERT(!passbuf.protected);
strncpynt(buf, passbuf.password, size);
purge_user_pass(&passbuf, false);

Expand Down Expand Up @@ -459,6 +461,7 @@ auth_user_pass_setup(const char *auth_file, bool is_inline,

if (!auth_user_pass.defined && !auth_token.defined)
{
unprotect_user_pass(&auth_user_pass);
#ifdef ENABLE_MANAGEMENT
if (auth_challenge) /* dynamic challenge/response */
{
Expand Down Expand Up @@ -2258,6 +2261,7 @@ key_method_2_write(struct buffer *buf, struct tls_multi *multi, struct tls_sessi
{
up = &auth_token;
}
unprotect_user_pass(up);

if (!write_string(buf, up->username, -1))
{
Expand All @@ -2270,8 +2274,11 @@ key_method_2_write(struct buffer *buf, struct tls_multi *multi, struct tls_sessi
/* save username for auth-token which may get pushed later */
if (session->opt->pull && up != &auth_token)
{
unprotect_user_pass(&auth_token);
strncpynt(auth_token.username, up->username, USER_PASS_LEN);
protect_user_pass(&auth_token);
}
protect_user_pass(up);
/* respect auth-nocache */
purge_user_pass(&auth_user_pass, false);
}
Expand Down
2 changes: 2 additions & 0 deletions src/openvpn/ssl_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -1594,6 +1594,8 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
{
struct key_state *ks = &session->key[KS_PRIMARY]; /* primary key */

ASSERT(up && !up->protected);

#ifdef ENABLE_MANAGEMENT
int man_def_auth = KMDA_UNDEF;

Expand Down
36 changes: 36 additions & 0 deletions src/openvpn/win32.c
Original file line number Diff line number Diff line change
Expand Up @@ -1686,4 +1686,40 @@ plugin_in_trusted_dir(const WCHAR *plugin_path)
return wcsnicmp(system_dir, plugin_path, wcslen(system_dir)) == 0;
}

bool
protect_buffer_win32(char *buf, size_t len)
{
bool ret;
if (len % CRYPTPROTECTMEMORY_BLOCK_SIZE)
{
msg(M_NONFATAL, "Error: Unable to encrypt memory: buffer size not a multiple of %d",
CRYPTPROTECTMEMORY_BLOCK_SIZE);
return false;
}
ret = CryptProtectMemory(buf, len, CRYPTPROTECTMEMORY_SAME_PROCESS);
if (!ret)
{
msg(M_NONFATAL | M_ERRNO, "Failed to encrypt memory.");
}
return ret;
}

bool
unprotect_buffer_win32(char *buf, size_t len)
{
bool ret;
if (len % CRYPTPROTECTMEMORY_BLOCK_SIZE)
{
msg(M_NONFATAL, "Error: Unable to decrypt memory: buffer size not a multiple of %d",
CRYPTPROTECTMEMORY_BLOCK_SIZE);
return false;
}
ret = CryptUnprotectMemory(buf, len, CRYPTPROTECTMEMORY_SAME_PROCESS);
if (!ret)
{
msg(M_FATAL | M_ERRNO, "Failed to decrypt memory.");
}
return ret;
}

#endif /* ifdef _WIN32 */
22 changes: 22 additions & 0 deletions src/openvpn/win32.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,5 +360,27 @@ get_openvpn_reg_value(const WCHAR *key, WCHAR *value, DWORD size);
bool
plugin_in_trusted_dir(const WCHAR *plugin_path);

/**
* Encrypt a region of memory using CryptProtectMemory()
* with access restricted to the current process.
*
* - buf pointer to the memory
* - len number of bytes to encrypt -- must be a multiple of
* CRYPTPROTECTMEMORY_BLOCK_SIZE = 16
*/
bool
protect_buffer_win32(char *buf, size_t len);

/**
* Decrypt a previously encrypted region of memory using CryptUnProtectMemory()
* with access restricted to the current process.
*
* - buf pointer to the memory
* - len number of bytes to encrypt -- must be a multiple of
* CRYPTPROTECTMEMORY_BLOCK_SIZE = 16
*/
bool
unprotect_buffer_win32(char *buf, size_t len);

#endif /* ifndef OPENVPN_WIN32_H */
#endif /* ifdef _WIN32 */

0 comments on commit 9e1598d

Please sign in to comment.