mirror of
https://github.com/OpenVPN/openvpn.git
synced 2025-05-08 21:25:53 +08:00
Implement challenge/response authentication support in client mode,
where credentials are entered from stdin. This capability is compiled when ENABLE_CLIENT_CR is defined in syshead.h (enabled by default). Challenge/response support was previously implemented for creds that are queried via the management interface. In this case, the challenge message will be returned as a custom client-reason-text string (see management-notes.txt for more info) on auth failure. Also, see the comments in misc.c above get_auth_challenge() for info on the OpenVPN challenge/response protocol. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@6568 e7ae566f-a301-0410-adde-c780ea21d3b5
This commit is contained in:
parent
c68e0cddf0
commit
3cf9dd88fd
2
base64.c
2
base64.c
@ -33,7 +33,7 @@
|
||||
|
||||
#include "syshead.h"
|
||||
|
||||
#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11)
|
||||
#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
|
160
misc.c
160
misc.c
@ -26,6 +26,7 @@
|
||||
|
||||
#include "buffer.h"
|
||||
#include "misc.h"
|
||||
#include "base64.h"
|
||||
#include "tun.h"
|
||||
#include "error.h"
|
||||
#include "thread.h"
|
||||
@ -1363,10 +1364,11 @@ get_console_input (const char *prompt, const bool echo, char *input, const int c
|
||||
*/
|
||||
|
||||
bool
|
||||
get_user_pass (struct user_pass *up,
|
||||
const char *auth_file,
|
||||
const char *prefix,
|
||||
const unsigned int flags)
|
||||
get_user_pass_cr (struct user_pass *up,
|
||||
const char *auth_file,
|
||||
const char *prefix,
|
||||
const unsigned int flags,
|
||||
const char *auth_challenge)
|
||||
{
|
||||
struct gc_arena gc = gc_new ();
|
||||
|
||||
@ -1379,7 +1381,7 @@ get_user_pass (struct user_pass *up,
|
||||
|
||||
#ifdef ENABLE_MANAGEMENT
|
||||
/*
|
||||
* Get username/password from standard input?
|
||||
* Get username/password from management interface?
|
||||
*/
|
||||
if (management
|
||||
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
|
||||
@ -1419,22 +1421,47 @@ get_user_pass (struct user_pass *up,
|
||||
*/
|
||||
else if (from_stdin)
|
||||
{
|
||||
struct buffer user_prompt = alloc_buf_gc (128, &gc);
|
||||
struct buffer pass_prompt = alloc_buf_gc (128, &gc);
|
||||
|
||||
buf_printf (&user_prompt, "Enter %s Username:", prefix);
|
||||
buf_printf (&pass_prompt, "Enter %s Password:", prefix);
|
||||
|
||||
if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
if (auth_challenge)
|
||||
{
|
||||
if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
|
||||
msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
|
||||
if (strlen (up->username) == 0)
|
||||
msg (M_FATAL, "ERROR: %s username is empty", prefix);
|
||||
}
|
||||
struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc);
|
||||
if (ac)
|
||||
{
|
||||
char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc);
|
||||
struct buffer packed_resp;
|
||||
|
||||
if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
|
||||
msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
|
||||
buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
|
||||
msg (M_INFO, "CHALLENGE: %s", ac->challenge_text);
|
||||
if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
|
||||
msg (M_FATAL, "ERROR: could not read challenge response from stdin");
|
||||
strncpynt (up->username, ac->user, USER_PASS_LEN);
|
||||
buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg (M_FATAL, "ERROR: received malformed challenge request from server");
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
struct buffer user_prompt = alloc_buf_gc (128, &gc);
|
||||
struct buffer pass_prompt = alloc_buf_gc (128, &gc);
|
||||
|
||||
buf_printf (&user_prompt, "Enter %s Username:", prefix);
|
||||
buf_printf (&pass_prompt, "Enter %s Password:", prefix);
|
||||
|
||||
if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
|
||||
{
|
||||
if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
|
||||
msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
|
||||
if (strlen (up->username) == 0)
|
||||
msg (M_FATAL, "ERROR: %s username is empty", prefix);
|
||||
}
|
||||
|
||||
if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
|
||||
msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1498,6 +1525,101 @@ get_user_pass (struct user_pass *up,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
|
||||
/*
|
||||
* Parse a challenge message returned along with AUTH_FAILED.
|
||||
* The message is formatted as such:
|
||||
*
|
||||
* CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
|
||||
*
|
||||
* flags: a series of optional, comma-separated flags:
|
||||
* E : echo the response when the user types it
|
||||
* R : a response is required
|
||||
*
|
||||
* state_id: an opaque string that should be returned to the server
|
||||
* along with the response.
|
||||
*
|
||||
* username_base64 : the username formatted as base64
|
||||
*
|
||||
* challenge_text : the challenge text to be shown to the user
|
||||
*
|
||||
* Example challenge:
|
||||
*
|
||||
* CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
|
||||
*
|
||||
* After showing the challenge_text and getting a response from the user
|
||||
* (if R flag is specified), the client should submit the following
|
||||
* auth creds back to the OpenVPN server:
|
||||
*
|
||||
* Username: [username decoded from username_base64]
|
||||
* Password: CRV1::<state_id>::<response_text>
|
||||
*
|
||||
* Where state_id is taken from the challenge request and response_text
|
||||
* is what the user entered in response to the challenge_text.
|
||||
* If the R flag is not present, response_text may be the empty
|
||||
* string.
|
||||
*
|
||||
* Example response (suppose the user enters "8675309" for the token PIN):
|
||||
*
|
||||
* Username: cr1 ("Y3Ix" base64 decoded)
|
||||
* Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
|
||||
*/
|
||||
struct auth_challenge_info *
|
||||
get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
|
||||
{
|
||||
if (auth_challenge)
|
||||
{
|
||||
struct auth_challenge_info *ac;
|
||||
const int len = strlen (auth_challenge);
|
||||
char *work = (char *) gc_malloc (len+1, false, gc);
|
||||
char *cp;
|
||||
|
||||
struct buffer b;
|
||||
buf_set_read (&b, (const uint8_t *)auth_challenge, len);
|
||||
|
||||
ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc);
|
||||
|
||||
/* parse prefix */
|
||||
if (!buf_parse(&b, ':', work, len))
|
||||
return NULL;
|
||||
if (strcmp(work, "CRV1"))
|
||||
return NULL;
|
||||
|
||||
/* parse flags */
|
||||
if (!buf_parse(&b, ':', work, len))
|
||||
return NULL;
|
||||
for (cp = work; *cp != '\0'; ++cp)
|
||||
{
|
||||
const char c = *cp;
|
||||
if (c == 'E')
|
||||
ac->flags |= CR_ECHO;
|
||||
else if (c == 'R')
|
||||
ac->flags |= CR_RESPONSE;
|
||||
}
|
||||
|
||||
/* parse state ID */
|
||||
if (!buf_parse(&b, ':', work, len))
|
||||
return NULL;
|
||||
ac->state_id = string_alloc(work, gc);
|
||||
|
||||
/* parse user name */
|
||||
if (!buf_parse(&b, ':', work, len))
|
||||
return NULL;
|
||||
ac->user = (char *) gc_malloc (strlen(work)+1, true, gc);
|
||||
base64_decode(work, (void*)ac->user);
|
||||
|
||||
/* parse challenge text */
|
||||
ac->challenge_text = string_alloc(BSTR(&b), gc);
|
||||
|
||||
return ac;
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if AUTO_USERID
|
||||
|
||||
static const char *
|
||||
|
38
misc.h
38
misc.h
@ -252,6 +252,26 @@ struct user_pass
|
||||
char password[USER_PASS_LEN];
|
||||
};
|
||||
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
/*
|
||||
* Challenge response info on client as pushed by server.
|
||||
*/
|
||||
struct auth_challenge_info {
|
||||
# define CR_ECHO (1<<0) /* echo response when typed by user */
|
||||
# define CR_RESPONSE (1<<1) /* response needed */
|
||||
unsigned int flags;
|
||||
|
||||
const char *user;
|
||||
const char *state_id;
|
||||
const char *challenge_text;
|
||||
};
|
||||
|
||||
struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc);
|
||||
|
||||
#else
|
||||
struct auth_challenge_info {};
|
||||
#endif
|
||||
|
||||
bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity);
|
||||
|
||||
/*
|
||||
@ -265,10 +285,20 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const
|
||||
#define GET_USER_PASS_NEED_STR (1<<5)
|
||||
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)
|
||||
|
||||
bool get_user_pass (struct user_pass *up,
|
||||
const char *auth_file,
|
||||
const char *prefix,
|
||||
const unsigned int flags);
|
||||
bool get_user_pass_cr (struct user_pass *up,
|
||||
const char *auth_file,
|
||||
const char *prefix,
|
||||
const unsigned int flags,
|
||||
const char *auth_challenge);
|
||||
|
||||
static inline bool
|
||||
get_user_pass (struct user_pass *up,
|
||||
const char *auth_file,
|
||||
const char *prefix,
|
||||
const unsigned int flags)
|
||||
{
|
||||
return get_user_pass_cr (up, auth_file, prefix, flags, NULL);
|
||||
}
|
||||
|
||||
void fail_user_pass (const char *prefix,
|
||||
const unsigned int flags,
|
||||
|
12
push.c
12
push.c
@ -68,8 +68,18 @@ receive_auth_failed (struct context *c, const struct buffer *buffer)
|
||||
if (buf_string_compare_advance (&buf, "AUTH_FAILED,") && BLEN (&buf))
|
||||
reason = BSTR (&buf);
|
||||
management_auth_failure (management, UP_TYPE_AUTH, reason);
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
struct buffer buf = *buffer;
|
||||
if (buf_string_match_head_str (&buf, "AUTH_FAILED,CRV1:") && BLEN (&buf))
|
||||
{
|
||||
buf_advance (&buf, 12); /* Length of "AUTH_FAILED," substring */
|
||||
ssl_put_auth_challenge (BSTR (&buf));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
27
ssl.c
27
ssl.c
@ -286,6 +286,10 @@ pem_password_callback (char *buf, int size, int rwflag, void *u)
|
||||
static bool auth_user_pass_enabled; /* GLOBAL */
|
||||
static struct user_pass auth_user_pass; /* GLOBAL */
|
||||
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
static char *auth_challenge; /* GLOBAL */
|
||||
#endif
|
||||
|
||||
void
|
||||
auth_user_pass_setup (const char *auth_file)
|
||||
{
|
||||
@ -294,6 +298,8 @@ auth_user_pass_setup (const char *auth_file)
|
||||
{
|
||||
#if AUTO_USERID
|
||||
get_user_pass_auto_userid (&auth_user_pass, auth_file);
|
||||
#elif defined(ENABLE_CLIENT_CR)
|
||||
get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
|
||||
#else
|
||||
get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
|
||||
#endif
|
||||
@ -321,8 +327,29 @@ ssl_purge_auth (void)
|
||||
#endif
|
||||
purge_user_pass (&passbuf, true);
|
||||
purge_user_pass (&auth_user_pass, true);
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
ssl_purge_auth_challenge();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
|
||||
void
|
||||
ssl_purge_auth_challenge (void)
|
||||
{
|
||||
free (auth_challenge);
|
||||
auth_challenge = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
ssl_put_auth_challenge (const char *cr_str)
|
||||
{
|
||||
ssl_purge_auth_challenge();
|
||||
auth_challenge = string_alloc(cr_str, NULL);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OpenSSL callback to get a temporary RSA key, mostly
|
||||
* used for export ciphers.
|
||||
|
11
ssl.h
11
ssl.h
@ -705,6 +705,17 @@ void auth_user_pass_setup (const char *auth_file);
|
||||
void ssl_set_auth_nocache (void);
|
||||
void ssl_purge_auth (void);
|
||||
|
||||
|
||||
#ifdef ENABLE_CLIENT_CR
|
||||
/*
|
||||
* ssl_get_auth_challenge will parse the server-pushed auth-failed
|
||||
* reason string and return a dynamically allocated
|
||||
* auth_challenge_info struct.
|
||||
*/
|
||||
void ssl_purge_auth_challenge (void);
|
||||
void ssl_put_auth_challenge (const char *cr_str);
|
||||
#endif
|
||||
|
||||
void tls_set_verify_command (const char *cmd);
|
||||
void tls_set_crl_verify (const char *crl);
|
||||
void tls_set_verify_x509name (const char *x509name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user