From c68e0cddf024ae5b18f89062bf7164da77f1cf06 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Sat, 23 Oct 2010 22:32:00 +0000 Subject: Fixed initialization bug in route_list_add_default_gateway (Gert Doering). git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@6566 e7ae566f-a301-0410-adde-c780ea21d3b5 --- route.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/route.c b/route.c index 5d8f8d6..20f62d5 100644 --- a/route.c +++ b/route.c @@ -450,6 +450,8 @@ init_route_list (struct route_list *rl, struct route r; int k; + CLEAR(netlist); /* init_route() will not always init this */ + if (!init_route (&r, &netlist, &opt->routes[i], -- cgit From 3cf9dd88fd84108eccfcce0ebf44e00f9481cd82 Mon Sep 17 00:00:00 2001 From: James Yonan Date: Sun, 24 Oct 2010 09:12:47 +0000 Subject: 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 --- base64.c | 2 +- misc.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------- misc.h | 38 +++++++++++++-- push.c | 12 ++++- ssl.c | 27 +++++++++++ ssl.h | 11 +++++ syshead.h | 5 ++ 7 files changed, 229 insertions(+), 24 deletions(-) diff --git a/base64.c b/base64.c index 7d876a6..2cc3944 100644 --- a/base64.c +++ b/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" diff --git a/misc.c b/misc.c index 507bcc2..f5a3b89 100644 --- a/misc.c +++ b/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; + + 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); - 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_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: 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:::: + * + * 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 * diff --git a/misc.h b/misc.h index 328107d..3cd7d9e 100644 --- a/misc.h +++ b/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, diff --git a/push.c b/push.c index 9ddc900..0db826a 100644 --- a/push.c +++ b/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 + } } } diff --git a/ssl.c b/ssl.c index a1268ac..dffe882 100644 --- a/ssl.c +++ b/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. diff --git a/ssl.h b/ssl.h index c6a5627..4373a80 100644 --- a/ssl.h +++ b/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); diff --git a/syshead.h b/syshead.h index 15445fc..bad5ce0 100644 --- a/syshead.h +++ b/syshead.h @@ -660,6 +660,11 @@ socket_defined (const socket_descriptor_t sd) #define AUTO_USERID 0 #endif +/* + * Do we support challenge/response authentication, as a console-based client? + */ +#define ENABLE_CLIENT_CR + /* * Do we support pushing peer info? */ -- cgit From d053e36df93aabcdb23e9971c48f990aa77bda4e Mon Sep 17 00:00:00 2001 From: James Yonan Date: Sun, 24 Oct 2010 09:17:24 +0000 Subject: Make base64.h have the same conditional compilation expression as base64.c. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@6569 e7ae566f-a301-0410-adde-c780ea21d3b5 --- base64.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base64.h b/base64.h index a966b2d..968d18d 100644 --- a/base64.h +++ b/base64.h @@ -34,7 +34,7 @@ #ifndef _BASE64_H_ #define _BASE64_H_ -#ifdef ENABLE_HTTP_PROXY +#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) int base64_encode(const void *data, int size, char **str); int base64_decode(const char *str, void *data); -- cgit From 20a4c12783144d798c6b7446bae8920fdfea7adc Mon Sep 17 00:00:00 2001 From: James Yonan Date: Thu, 28 Oct 2010 05:01:24 +0000 Subject: Version 2.1.3b git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@6585 e7ae566f-a301-0410-adde-c780ea21d3b5 --- version.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.m4 b/version.m4 index f0541e3..c04c6cf 100644 --- a/version.m4 +++ b/version.m4 @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1.3a]) +define(PRODUCT_VERSION,[2.1.3b]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) -- cgit