diff options
Diffstat (limited to 'misc.c')
-rw-r--r-- | misc.c | 158 |
1 files changed, 140 insertions, 18 deletions
@@ -26,6 +26,7 @@ #include "buffer.h" #include "misc.h" +#include "base64.h" #include "tun.h" #include "error.h" #include "thread.h" @@ -1397,10 +1398,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 (); @@ -1413,7 +1415,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))) @@ -1453,22 +1455,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 { @@ -1532,6 +1559,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 * |